diff --git a/src/dynamic_data/libraries/dynamic_library.rs b/src/dynamic_data/libraries/dynamic_library.rs index 3fc0874..3acc11e 100755 --- a/src/dynamic_data/libraries/dynamic_library.rs +++ b/src/dynamic_data/libraries/dynamic_library.rs @@ -156,7 +156,7 @@ pub mod test { static_data: Arc::new(crate::static_data::libraries::static_data::test::build()), stat_calculator: Arc::new(Gen7BattleStatCalculator::new()), damage_calculator: Arc::new(Gen7DamageLibrary::new(false)), - misc_library: Arc::new(Gen7MiscLibrary::new()), + misc_library: Arc::new(Gen7MiscLibrary::default()), script_resolver: Arc::new(EmptyScriptResolver {}), } } diff --git a/src/dynamic_data/libraries/misc_library.rs b/src/dynamic_data/libraries/misc_library.rs index 48a6872..9b420ec 100755 --- a/src/dynamic_data/libraries/misc_library.rs +++ b/src/dynamic_data/libraries/misc_library.rs @@ -1,4 +1,5 @@ -use std::fmt::Debug; +use chrono::Timelike; +use std::fmt::{Debug, Formatter}; use std::sync::Arc; use hashbrown::HashSet; @@ -6,7 +7,7 @@ use hashbrown::HashSet; use crate::dynamic_data::choices::{MoveChoice, TurnChoice}; use crate::dynamic_data::Pokemon; use crate::dynamic_data::{LearnedMove, MoveLearnMethod}; -use crate::static_data::{MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffectImpl}; +use crate::static_data::{MoveCategory, MoveData, MoveDataImpl, MoveTarget, SecondaryEffectImpl, TimeOfDay}; use crate::StringKey; /// The misc library holds several misc functions required for the battle to run. @@ -16,19 +17,24 @@ pub trait MiscLibrary: Debug { /// Returns the move we need to use if we can't use another move. Typically Struggle. fn replacement_move(&self, user: &Pokemon, target_side: u8, target_index: u8) -> TurnChoice; // TODO: can evolve from level up? - // TODO: get time + /// Gets the current time of day for the battle. + fn time_of_day(&self) -> TimeOfDay; } +/// A function pointer to get the time of day. +type GetTimeOfDayFn = Box TimeOfDay>; + /// A gen 7 implementation for the MiscLibrary. -#[derive(Debug)] pub struct Gen7MiscLibrary { /// The learned move data for struggle. struggle_learned_move: Arc, + /// The function to get the time of day. + get_time_fn: GetTimeOfDayFn, } impl Gen7MiscLibrary { /// Instantiates a new MiscLibrary. - pub fn new() -> Self { + pub fn new(get_time_fn: GetTimeOfDayFn) -> Self { let struggle_data: Arc = Arc::new(MoveDataImpl::new( &StringKey::new("struggle"), 0.into(), @@ -46,13 +52,28 @@ impl Gen7MiscLibrary { HashSet::new(), )); let struggle_learned_move = Arc::new(LearnedMove::new(struggle_data, MoveLearnMethod::Unknown)); - Self { struggle_learned_move } + Self { + struggle_learned_move, + get_time_fn, + } } } impl Default for Gen7MiscLibrary { fn default() -> Self { - Self::new() + Self::new(Box::new(|| { + let time = chrono::Local::now().time(); + let hour = time.hour(); + // Following the values for Pokemon Sun. + match hour { + 0..=5 => TimeOfDay::Night, + 6..=9 => TimeOfDay::Morning, + 10..=16 => TimeOfDay::Day, + 17 => TimeOfDay::Evening, + 18..=23 => TimeOfDay::Night, + _ => unreachable!(), + } + })) } } @@ -70,4 +91,14 @@ impl MiscLibrary for Gen7MiscLibrary { target_index, )) } + + fn time_of_day(&self) -> TimeOfDay { + (self.get_time_fn)() + } +} + +impl Debug for Gen7MiscLibrary { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("Gen7MiscLibrary") + } } diff --git a/src/ffi/dynamic_data/libraries/misc_library.rs b/src/ffi/dynamic_data/libraries/misc_library.rs index 7f5a402..1190abb 100644 --- a/src/ffi/dynamic_data/libraries/misc_library.rs +++ b/src/ffi/dynamic_data/libraries/misc_library.rs @@ -1,10 +1,46 @@ use crate::dynamic_data::{Gen7MiscLibrary, MiscLibrary}; use crate::ffi::ffi_handle::FFIHandle; +use crate::static_data::TimeOfDay; use std::sync::Arc; /// Instantiates a new MiscLibrary. #[no_mangle] -extern "C" fn gen_7_misc_library_new() -> FFIHandle> { - let v: Arc = Arc::new(Gen7MiscLibrary::new()); +extern "C" fn gen_7_misc_library_new(get_time_of_day_fn: FFIGetTimeOfDayFn) -> FFIHandle> { + let v: Arc = Arc::new(Gen7MiscLibrary::new(Box::new(get_time_of_day_fn))); FFIHandle::get_handle(v.into()) } + +/// Wrapper class for easier use of an external function pointer. +#[repr(C)] +pub(super) struct FFIGetTimeOfDayFn { + /// The actual C function to be called. Note that we pass the batch id as a pair of u64s. This + /// is because u128 does not have a stable ABI yet. + f: extern "C" fn() -> u8, +} + +impl FFIGetTimeOfDayFn { + /// Calls the actual wrapped function in the correct format. + fn call_self(&self) -> TimeOfDay { + unsafe { std::mem::transmute((self.f)()) } + } +} + +impl FnMut<()> for FFIGetTimeOfDayFn { + extern "rust-call" fn call_mut(&mut self, _: ()) -> Self::Output { + self.call_self() + } +} + +impl FnOnce<()> for FFIGetTimeOfDayFn { + type Output = TimeOfDay; + + extern "rust-call" fn call_once(self, _: ()) -> Self::Output { + self.call_self() + } +} + +impl Fn<()> for FFIGetTimeOfDayFn { + extern "rust-call" fn call(&self, _: ()) -> Self::Output { + self.call_self() + } +} diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs index f617755..0eb4158 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/mod.rs @@ -1,11 +1,11 @@ -use crate::dynamic_data::{DynamicLibrary, ScriptOwnerData}; +use crate::dynamic_data::{DynamicLibrary, MiscLibrary, ScriptOwnerData}; use crate::script_implementations::wasm::export_registry::register; use crate::script_implementations::wasm::export_registry::wasm_result::{try_wasm, wasm_ok, WasmResult}; use crate::script_implementations::wasm::extern_ref::ExternRef; use wasmer::{FunctionEnv, FunctionEnvMut, Imports, StoreMut}; use crate::script_implementations::wasm::script_resolver::WebAssemblyEnv; -use crate::static_data::StaticData; +use crate::static_data::{StaticData, TimeOfDay}; /// The battle registration mod battle; @@ -38,6 +38,27 @@ register! { wasm_ok(ExternRef::::func_new(&env, static_data.into())) } + fn dynamic_library_get_misc_library( + env: FunctionEnvMut, + dynamic_lib: ExternRef, + ) -> WasmResult> { + let dynamic_lib = try_wasm!(dynamic_lib.value_func_arc(&env), env); + let misc_library = dynamic_lib.misc_library(); + wasm_ok(ExternRef::::func_new(&env, misc_library.into())) + } + + fn misc_library_get_time_of_day( + env: FunctionEnvMut, + dynamic_lib: ExternRef, + ) -> WasmResult { + let misc_library = try_wasm!(dynamic_lib.value_func_arc(&env), env); + unsafe{ + let time_of_day = misc_library.time_of_day(); + wasm_ok(std::mem::transmute::(time_of_day)) + } + } + + fn script_get_owner( env: FunctionEnvMut, script: u32, diff --git a/src/script_implementations/wasm/export_registry/wasm_object.rs b/src/script_implementations/wasm/export_registry/wasm_object.rs index f098f5a..dd2a9ab 100644 --- a/src/script_implementations/wasm/export_registry/wasm_object.rs +++ b/src/script_implementations/wasm/export_registry/wasm_object.rs @@ -40,6 +40,7 @@ pub(crate) enum WasmObject { // Dynamic data libraries DynamicLibrary(Weak), + MiscLibrary(Weak), // Battle data Battle(WeakBattleReference), @@ -88,6 +89,7 @@ impl PartialEq for WasmObject { (WasmObject::PokemonParty(s1), WasmObject::PokemonParty(s2)) => Weak::ptr_eq(s1, s2), (WasmObject::LearnedMove(s1), WasmObject::LearnedMove(s2)) => Weak::ptr_eq(s1, s2), (WasmObject::DynamicLibrary(s1), WasmObject::DynamicLibrary(s2)) => Weak::ptr_eq(s1, s2), + (WasmObject::MiscLibrary(s1), WasmObject::MiscLibrary(s2)) => Weak::ptr_eq(s1, s2), (WasmObject::Battle(s1), WasmObject::Battle(s2)) => WeakBattleReference::eq(s1, s2), (WasmObject::ChoiceQueue(s1), WasmObject::ChoiceQueue(s2)) => Weak::ptr_eq(s1, s2), (WasmObject::BattleRandom(s1), WasmObject::BattleRandom(s2)) => Weak::ptr_eq(s1, s2), @@ -131,6 +133,7 @@ impl Hash for WasmObject { WasmObject::PokemonParty(m) => m.as_ptr().hash(state), WasmObject::LearnedMove(m) => m.as_ptr().hash(state), WasmObject::DynamicLibrary(m) => m.as_ptr().hash(state), + WasmObject::MiscLibrary(m) => m.as_ptr().hash(state), WasmObject::Battle(m) => m.as_ptr().hash(state), WasmObject::ChoiceQueue(m) => m.as_ptr().hash(state), WasmObject::BattleRandom(m) => m.as_ptr().hash(state), @@ -394,6 +397,14 @@ impl From<&Arc> for WasmObject { impl_from_wasm_obj!(DynamicLibrary, Arc); +impl From<&Arc> for WasmObject { + fn from(value: &Arc) -> Self { + Self::MiscLibrary(Arc::downgrade(value)) + } +} + +impl_from_wasm_obj!(MiscLibrary, Arc); + impl From<&Arc> for WasmObject { fn from(value: &Arc) -> Self { Self::BattleRandom(Arc::downgrade(value)) diff --git a/src/script_implementations/wasm/extern_ref.rs b/src/script_implementations/wasm/extern_ref.rs index 8b56ce4..dfd2570 100755 --- a/src/script_implementations/wasm/extern_ref.rs +++ b/src/script_implementations/wasm/extern_ref.rs @@ -168,17 +168,6 @@ impl Default for VecExternRef { } } -// impl VecExternRef { -// /// Instantiates a new VecExternRef for a given slice. -// pub fn new(env: &WebAssemblyEnvironmentData, value: &Vec) -> Self { -// Self { -// index: env.get_extern_vec_ref_index(value), -// size: value.len() as u32, -// _phantom: Default::default(), -// } -// } -// } - unsafe impl FromToNativeWasmType for VecExternRef { type Native = i64; diff --git a/src/static_data/mod.rs b/src/static_data/mod.rs index c2412ec..777c76d 100755 --- a/src/static_data/mod.rs +++ b/src/static_data/mod.rs @@ -15,6 +15,9 @@ pub use species_data::*; pub use statistic_set::*; #[doc(inline)] pub use statistics::*; +#[doc(inline)] +pub use time_of_day::*; + use std::fmt::{Display, Formatter}; #[cfg(test)] @@ -50,6 +53,8 @@ mod species_data; mod statistic_set; /// Statistics are numerical values on Pokemon that are used in battle. mod statistics; +/// Time of day defines the time of day for a battle. +mod time_of_day; /// A parameter for an effect. This is basically a simple way to dynamically store multiple different /// primitives on data. diff --git a/src/static_data/time_of_day.rs b/src/static_data/time_of_day.rs new file mode 100644 index 0000000..552373d --- /dev/null +++ b/src/static_data/time_of_day.rs @@ -0,0 +1,14 @@ +/// The time of day. These values are the 4 different groups of time of day in Pokemon games since +/// gen 5. The exact times these correspond to differ between games. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[repr(u8)] +pub enum TimeOfDay { + /// The morning. + Morning = 0, + /// The day. + Day = 1, + /// The evening. + Evening = 2, + /// The night. + Night = 3, +} diff --git a/tests/common/library_loader.rs b/tests/common/library_loader.rs index bc8a8e6..6aa0b35 100755 --- a/tests/common/library_loader.rs +++ b/tests/common/library_loader.rs @@ -20,7 +20,7 @@ use pkmn_lib::static_data::{ GrowthRateLibrary, GrowthRateLibraryImpl, ItemImpl, ItemLibrary, ItemLibraryImpl, LearnableMoves, LearnableMovesImpl, LibrarySettingsImpl, LookupGrowthRate, MoveDataImpl, MoveLibrary, MoveLibraryImpl, NatureImpl, NatureLibrary, NatureLibraryImpl, SecondaryEffect, SecondaryEffectImpl, SpeciesImpl, SpeciesLibrary, - SpeciesLibraryImpl, StaticDataImpl, StaticStatisticSet, Statistic, TypeLibrary, TypeLibraryImpl, + SpeciesLibraryImpl, StaticDataImpl, StaticStatisticSet, Statistic, TimeOfDay, TypeLibrary, TypeLibraryImpl, }; use pkmn_lib::StringKey; @@ -84,7 +84,7 @@ pub fn load_library() -> LoadResult { Arc::new(data), Arc::new(Gen7BattleStatCalculator::new()), Arc::new(Gen7DamageLibrary::new(false)), - Arc::new(Gen7MiscLibrary::new()), + Arc::new(Gen7MiscLibrary::new(Box::new(|| TimeOfDay::Day))), script_resolver, )); diff --git a/tests/data/gen7_scripts.wasm b/tests/data/gen7_scripts.wasm index a1b304c..60731f5 100755 Binary files a/tests/data/gen7_scripts.wasm and b/tests/data/gen7_scripts.wasm differ