diff --git a/src/dynamic_data/event_hooks.rs b/src/dynamic_data/event_hooks.rs index 3b1b980..4dc0676 100755 --- a/src/dynamic_data/event_hooks.rs +++ b/src/dynamic_data/event_hooks.rs @@ -9,7 +9,7 @@ use crate::static_data::Species; use crate::static_data::{Form, Statistic}; /// A function that will be called when an event occured. -type EvtHookFn = Box)>; +type EvtHookFn = Box, EventBatchId)>; /// A collection of event hooks. type EvtHookCollection = Vec; @@ -35,12 +35,12 @@ impl EventHook { /// Run a new event. This will send the event to all externally defined event listeners. It will /// dispose of the event afterwards. - pub fn trigger(&self, evt: Event) { - let b = Box::new(&evt); + pub fn trigger(&self, evt: EventData, batch_id: EventBatchId) { + let b = Arc::new(evt); #[allow(clippy::unwrap_used)] // This should never fail. let read_lock = self.evt_hook_function.read().unwrap(); for f in read_lock.iter() { - f(&b); + f(b.clone(), batch_id); } } } @@ -53,7 +53,7 @@ impl Debug for EventHook { /// The different events that can occur during the battle, for which a GUI should show something. #[derive(Debug)] -pub enum Event<'own> { +pub enum EventData { /// A switch event happens when a Pokemon gets switched out for something else. Switch { /// The side the Pokemon got switched from/on @@ -77,7 +77,7 @@ pub enum Event<'own> { /// enjoy. SpeciesChange { /// The pokemon that changed species. - pokemon: &'own Pokemon, + pokemon: Pokemon, /// The new species of the Pokemon. species: Arc, /// The form of the species the Pokemon will have. @@ -86,14 +86,14 @@ pub enum Event<'own> { /// This event happens when a Pokemon changes form during battle. This is rather common. FormChange { /// The pokemon that changed forms. - pokemon: &'own Pokemon, + pokemon: Pokemon, /// The new form of the Pokemon. form: Arc, }, /// This event happens when a Pokemon takes damage. Damage { /// The Pokemon that takes damage. - pokemon: &'own Pokemon, + pokemon: Pokemon, /// The source of damage. source: DamageSource, /// The health of the Pokemon before the damage. @@ -104,7 +104,7 @@ pub enum Event<'own> { /// This event happens when a Pokemon gets healed Heal { /// The Pokemon that gets healed. - pokemon: &'own Pokemon, + pokemon: Pokemon, /// The health of the Pokemon before the heal. original_health: u32, /// The health of the Pokemon after the heal. @@ -113,24 +113,24 @@ pub enum Event<'own> { /// This event happens when a Pokemon faints. Faint { /// The pokemon that has fainted. - pokemon: &'own Pokemon, + pokemon: Pokemon, }, /// This event happens when a Pokemon uses a move on a target, just before any hits. MoveUse { /// The data of the move used. - executing_move: &'own ExecutingMove, + executing_move: Arc, }, /// This event happens when a Pokemon missed. Miss { /// The pokemon that missed. - user: &'own Pokemon, + user: Pokemon, }, /// The turn is finished running, waiting for new input. EndTurn, /// A pokemon had its stat boost changed StatBoostChange { /// The pokemon that had its stat boosts changed. - user: &'own Pokemon, + user: Pokemon, /// The statistic that changed. stat: Statistic, /// The value of the stat before the change. @@ -138,4 +138,42 @@ pub enum Event<'own> { /// The value of the stat after the change. new_value: i8, }, + /// Triggered when a move hits. + MoveHit { + /// The pokemon that used the move. + user: Pokemon, + /// The pokemon that was hit. + target: Pokemon, + /// The effectiveness of the move. + effectiveness: f32, + }, +} + +/// Allows for the batching of multiple events that should be shown at the same time. +#[derive(Default, Copy, Clone)] +pub struct EventBatchId { + /// The id of the batch. + batch_id: uuid::Uuid, +} + +impl EventBatchId { + /// Gets the batch id as two u64s. + pub fn as_u64_pair(&self) -> (u64, u64) { + self.batch_id.as_u64_pair() + } + + /// Creates a new batch id from two u64s. + pub fn from_u64_pair(a: u64, b: u64) -> Self { + Self { + batch_id: uuid::Uuid::from_u64_pair(a, b), + } + } +} + +impl From for EventBatchId { + fn from(value: u128) -> Self { + Self { + batch_id: uuid::Uuid::from_u128(value), + } + } } diff --git a/src/dynamic_data/flow/turn_runner.rs b/src/dynamic_data/flow/turn_runner.rs index 903ab71..4ff8be3 100755 --- a/src/dynamic_data/flow/turn_runner.rs +++ b/src/dynamic_data/flow/turn_runner.rs @@ -4,13 +4,13 @@ use std::ops::Deref; use std::sync::Arc; use crate::dynamic_data::choices::TurnChoice; -use crate::dynamic_data::event_hooks::Event; +use crate::dynamic_data::event_hooks::EventData; use crate::dynamic_data::flow::target_resolver::resolve_targets; use crate::dynamic_data::script_handling::{ScriptSource, ScriptWrapper}; -use crate::dynamic_data::Battle; use crate::dynamic_data::DamageSource; use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::Pokemon; +use crate::dynamic_data::{Battle, EventBatchId}; use crate::static_data::MoveCategory; use crate::{run_scripts, script_hook, PkmnError}; @@ -145,9 +145,12 @@ impl Battle { if !executing_move.chosen_move().try_use(1) { return Ok(()); } - self.event_hook().trigger(Event::MoveUse { - executing_move: &executing_move, - }); + self.event_hook().trigger( + EventData::MoveUse { + executing_move: executing_move.clone(), + }, + Default::default(), + ); let mut fail = false; script_hook!(fail_move, executing_move, &executing_move, &mut fail); if fail { @@ -280,9 +283,12 @@ impl Battle { // TODO: Deal with accuracy/evasion stats. if accuracy < 100 && self.random().get_max(100)? as u8 >= accuracy { script_hook!(on_move_miss, target, executing_move, target); - self.event_hook().trigger(Event::Miss { - user: executing_move.user().deref(), - }); + self.event_hook().trigger( + EventData::Miss { + user: executing_move.user().clone(), + }, + Default::default(), + ); break; } @@ -306,7 +312,16 @@ impl Battle { hit_data.set_damage(damage); } if damage > 0 { - target.damage(damage, DamageSource::MoveDamage)?; + let move_hit_event_batch = EventBatchId::default(); + self.event_hook().trigger( + EventData::MoveHit { + user: executing_move.user().clone(), + target: target.clone(), + effectiveness, + }, + move_hit_event_batch, + ); + target.damage(damage, DamageSource::MoveDamage, move_hit_event_batch)?; if !target.is_fainted() { script_hook!(on_incoming_hit, target, executing_move, target, hit_index); } else { @@ -352,9 +367,12 @@ impl Battle { if number_of_hits == 0 { script_hook!(on_move_miss, target, executing_move, target); - self.event_hook().trigger(Event::Miss { - user: executing_move.user().deref(), - }); + self.event_hook().trigger( + EventData::Miss { + user: executing_move.user().clone(), + }, + Default::default(), + ); } if !executing_move.user().is_fainted() { diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 117a29b..24419ea 100755 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -9,7 +9,7 @@ use atomig::Atomic; use parking_lot::RwLock; use crate::dynamic_data::choices::TurnChoice; -use crate::dynamic_data::event_hooks::{Event, EventHook}; +use crate::dynamic_data::event_hooks::{EventData, EventHook}; use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::battle_random::BattleRandom; use crate::dynamic_data::models::battle_side::BattleSide; @@ -333,7 +333,7 @@ impl Battle { } self.data.current_turn_queue.write().take(); - self.data.event_hook.trigger(Event::EndTurn); + self.data.event_hook.trigger(EventData::EndTurn, Default::default()); let end_time = chrono::Utc::now(); let time = end_time - start_time; match time.num_nanoseconds() { diff --git a/src/dynamic_data/models/battle_side.rs b/src/dynamic_data/models/battle_side.rs index 4fe0eaf..fddca8e 100755 --- a/src/dynamic_data/models/battle_side.rs +++ b/src/dynamic_data/models/battle_side.rs @@ -8,7 +8,7 @@ use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; use crate::dynamic_data::choices::TurnChoice; -use crate::dynamic_data::event_hooks::Event; +use crate::dynamic_data::event_hooks::EventData; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; @@ -231,18 +231,24 @@ impl BattleSide { pokemon.mark_opponent_as_seen(opponent.weak()); } } - battle.event_hook().trigger(Event::Switch { - side_index: self.data.index, - index, - pokemon: Some(pokemon.clone()), - }); + battle.event_hook().trigger( + EventData::Switch { + side_index: self.data.index, + index, + pokemon: Some(pokemon.clone()), + }, + Default::default(), + ); script_hook!(on_switch_in, pokemon, pokemon); } else { - self.battle()?.event_hook().trigger(Event::Switch { - side_index: self.data.index, - index, - pokemon: None, - }); + self.battle()?.event_hook().trigger( + EventData::Switch { + side_index: self.data.index, + index, + pokemon: None, + }, + Default::default(), + ); } Ok(()) } @@ -339,11 +345,14 @@ impl BattleSide { } data.pokemon.write().swap(a as usize, b as usize); - self.battle()?.event_hook().trigger(Event::Swap { - side_index: data.index, - index_a: a, - index_b: b, - }); + self.battle()?.event_hook().trigger( + EventData::Swap { + side_index: data.index, + index_a: a, + index_b: b, + }, + Default::default(), + ); Ok(true) } diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index b7bbafd..950a6a6 100755 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -9,12 +9,13 @@ use parking_lot::lock_api::RwLockReadGuard; use parking_lot::{RawRwLock, RwLock}; use crate::defines::{LevelInt, MAX_MOVES}; -use crate::dynamic_data::event_hooks::Event; +use crate::dynamic_data::event_hooks::EventData; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod}; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::dynamic_data::{ - DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner, WeakBattleReference, + DynamicLibrary, EventBatchId, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScriptsOwner, + WeakBattleReference, }; use crate::static_data::AbilityIndex; use crate::static_data::Form; @@ -396,12 +397,15 @@ impl Pokemon { if changed { if let Some(battle) = self.get_battle() { let new_value = self.stat_boost(stat); - battle.event_hook().trigger(Event::StatBoostChange { - user: self, - stat, - old_value, - new_value, - }) + battle.event_hook().trigger( + EventData::StatBoostChange { + user: self.clone(), + stat, + old_value, + new_value, + }, + Default::default(), + ) } self.recalculate_boosted_stats()?; } @@ -531,11 +535,14 @@ impl Pokemon { let r = self.data.battle_data.read(); if let Some(battle_data) = &r.deref() { if let Some(battle) = battle_data.battle() { - battle.event_hook().trigger(Event::SpeciesChange { - pokemon: self, - species, - form, - }) + battle.event_hook().trigger( + EventData::SpeciesChange { + pokemon: self.clone(), + species, + form, + }, + Default::default(), + ) } } Ok(()) @@ -597,10 +604,13 @@ impl Pokemon { let r = self.data.battle_data.read(); if let Some(battle_data) = r.deref() { if let Some(battle) = battle_data.battle() { - battle.event_hook().trigger(Event::FormChange { - pokemon: self, - form: form.clone(), - }) + battle.event_hook().trigger( + EventData::FormChange { + pokemon: self.clone(), + form: form.clone(), + }, + Default::default(), + ) } }; Ok(()) @@ -679,7 +689,7 @@ impl Pokemon { } /// Damages the Pokemon by a certain amount of damage, from a damage source. - pub fn damage(&self, mut damage: u32, source: DamageSource) -> Result<()> { + pub fn damage(&self, mut damage: u32, source: DamageSource, evt_batch_id: EventBatchId) -> Result<()> { if damage > self.current_health() { damage = self.current_health(); } @@ -689,12 +699,15 @@ impl Pokemon { let new_health = self.current_health() - damage; if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { - battle.event_hook().trigger(Event::Damage { - pokemon: self, - source, - original_health: self.current_health(), - new_health, - }); + battle.event_hook().trigger( + EventData::Damage { + pokemon: self.clone(), + source, + original_health: self.current_health(), + new_health, + }, + evt_batch_id, + ); } } if self @@ -719,7 +732,9 @@ impl Pokemon { let r = self.data.battle_data.read(); if let Some(battle_data) = r.deref() { if let Some(battle) = battle_data.battle() { - battle.event_hook().trigger(Event::Faint { pokemon: self }); + battle + .event_hook() + .trigger(EventData::Faint { pokemon: self.clone() }, Default::default()); script_hook!(on_faint, self, self, source); script_hook!(on_remove, self,); @@ -752,11 +767,14 @@ impl Pokemon { let new_health = self.current_health() + max_amount; if let Some(battle_data) = &self.data.battle_data.read().deref() { if let Some(battle) = battle_data.battle() { - battle.event_hook().trigger(Event::Heal { - pokemon: self, - original_health: self.current_health(), - new_health, - }); + battle.event_hook().trigger( + EventData::Heal { + pokemon: self.clone(), + original_health: self.current_health(), + new_health, + }, + Default::default(), + ); } } self.data.current_health.store(new_health, Ordering::SeqCst); diff --git a/src/ffi/dynamic_data/models/event.rs b/src/ffi/dynamic_data/models/event.rs index a98f482..9892275 100644 --- a/src/ffi/dynamic_data/models/event.rs +++ b/src/ffi/dynamic_data/models/event.rs @@ -1,22 +1,25 @@ -use crate::dynamic_data::{Event, Pokemon}; -use crate::ffi::ExternPointer; +use crate::dynamic_data::{EventData, Pokemon}; +use crate::ffi::ffi_handle::FromFFIHandle; use crate::ffi::FFIHandle; +use std::ops::Deref; +use std::sync::Arc; /// The kind of the event. #[no_mangle] -extern "C" fn event_kind(ptr: ExternPointer) -> u8 { - match ptr.as_ref() { - Event::Switch { .. } => 1, - Event::Swap { .. } => 2, - Event::SpeciesChange { .. } => 3, - Event::FormChange { .. } => 4, - Event::Damage { .. } => 5, - Event::Heal { .. } => 6, - Event::Faint { .. } => 7, - Event::MoveUse { .. } => 8, - Event::Miss { .. } => 9, - Event::EndTurn => 10, - Event::StatBoostChange { .. } => 11, +extern "C" fn event_kind(ptr: FFIHandle>) -> u8 { + match ptr.from_ffi_handle().deref() { + EventData::Switch { .. } => 1, + EventData::Swap { .. } => 2, + EventData::SpeciesChange { .. } => 3, + EventData::FormChange { .. } => 4, + EventData::Damage { .. } => 5, + EventData::Heal { .. } => 6, + EventData::Faint { .. } => 7, + EventData::MoveUse { .. } => 8, + EventData::Miss { .. } => 9, + EventData::EndTurn => 10, + EventData::StatBoostChange { .. } => 11, + EventData::MoveHit { .. } => 12, } } @@ -43,12 +46,12 @@ macro_rules! event_ffi { } /// The getter for the data for the relevant event. #[no_mangle] - extern "C" fn [](ptr: ExternPointer) -> [<$event_name Data>] { - if let Event::$event_name { + extern "C" fn [](ptr: FFIHandle>) -> [<$event_name Data>] { + if let EventData::$event_name { $( $par_name, )+ - } = ptr.as_ref() + } = ptr.from_ffi_handle().deref() { $ctor } diff --git a/src/ffi/dynamic_data/models/native_event_hook.rs b/src/ffi/dynamic_data/models/native_event_hook.rs index 929302e..c8e83ae 100644 --- a/src/ffi/dynamic_data/models/native_event_hook.rs +++ b/src/ffi/dynamic_data/models/native_event_hook.rs @@ -1,25 +1,29 @@ -use crate::dynamic_data::Event; +use crate::dynamic_data::{EventBatchId, EventData}; +use crate::ffi::ffi_handle::FFIHandle; +use std::sync::Arc; /// Wrapper class for easier use of an external function pointer. #[repr(C)] pub(super) struct NativeEventHook { - /// The actual C function to be called. - f: extern "C" fn(*const Event<'_>), + /// 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(FFIHandle>, u64, u64), } impl NativeEventHook { /// Calls the actual wrapped function in the correct format. - fn call_self(&self, b: &&Event<'_>) { - (self.f)(*b as *const Event) + fn call_self(&self, b: Arc, batch_id: EventBatchId) { + let batch_id = batch_id.as_u64_pair(); + (self.f)(FFIHandle::get_handle(b.into()), batch_id.0, batch_id.1); } } /// A tuple with the arguments of the event hook function -type EventHookArgs<'a, 'b, 'c> = (&'a Box<&'b Event<'c>>,); +type EventHookArgs<'a, 'b, 'c> = (Arc, EventBatchId); impl FnMut> for NativeEventHook { extern "rust-call" fn call_mut(&mut self, args: EventHookArgs<'_, '_, '_>) -> Self::Output { - self.call_self(args.0) + self.call_self(args.0, args.1) } } @@ -27,12 +31,12 @@ impl FnOnce> for NativeEventHook { type Output = (); extern "rust-call" fn call_once(self, args: EventHookArgs<'_, '_, '_>) -> Self::Output { - self.call_self(args.0) + self.call_self(args.0, args.1) } } impl Fn> for NativeEventHook { extern "rust-call" fn call(&self, args: EventHookArgs<'_, '_, '_>) -> Self::Output { - self.call_self(args.0) + self.call_self(args.0, args.1) } } diff --git a/src/ffi/dynamic_data/models/pokemon.rs b/src/ffi/dynamic_data/models/pokemon.rs index 4d254a3..64ceaf0 100644 --- a/src/ffi/dynamic_data/models/pokemon.rs +++ b/src/ffi/dynamic_data/models/pokemon.rs @@ -1,5 +1,5 @@ use crate::defines::LevelInt; -use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, LearnedMove, MoveLearnMethod, Pokemon}; +use crate::dynamic_data::{Battle, DamageSource, DynamicLibrary, EventBatchId, LearnedMove, MoveLearnMethod, Pokemon}; use crate::ffi::ffi_handle::{FFIHandle, FromFFIHandle}; use crate::ffi::{FFIResult, OwnedPtrString}; use crate::static_data::{ @@ -397,8 +397,21 @@ extern "C" fn pokemon_is_on_battlefield(handle: FFIHandle) -> u8 { /// Damages the Pokemon by a certain amount of damage, from a damage source. #[no_mangle] -extern "C" fn pokemon_damage(handle: FFIHandle, damage: u32, source: DamageSource) -> FFIResult<()> { - handle.from_ffi_handle().damage(damage, source).into() +extern "C" fn pokemon_damage( + handle: FFIHandle, + damage: u32, + source: DamageSource, + evt_batch_id_1: u64, + evt_batch_id_2: u64, +) -> FFIResult<()> { + handle + .from_ffi_handle() + .damage( + damage, + source, + EventBatchId::from_u64_pair(evt_batch_id_1, evt_batch_id_2), + ) + .into() } /// Heals the Pokemon by a specific amount. Unless allow_revive is set to true, this will not diff --git a/src/ffi/ffi_handle.rs b/src/ffi/ffi_handle.rs index ec19b88..91bf6b6 100644 --- a/src/ffi/ffi_handle.rs +++ b/src/ffi/ffi_handle.rs @@ -100,6 +100,9 @@ pub(super) enum FFIObject { MiscLibrary(Arc), ScriptResolver(Arc), DynamicLibrary(Arc), + + // Events + Event(Arc), } unsafe impl Send for FFIObject {} @@ -218,6 +221,7 @@ impl Hash for FFIObject { Self::MiscLibrary(a) => Arc::as_ptr(a).hash(state), Self::ScriptResolver(a) => Arc::as_ptr(a).hash(state), Self::DynamicLibrary(a) => Arc::as_ptr(a).hash(state), + FFIObject::Event(a) => Arc::as_ptr(a).hash(state), } } } @@ -265,6 +269,7 @@ impl PartialEq for FFIObject { (Self::MiscLibrary(a), Self::MiscLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(), (Self::ScriptResolver(a), Self::ScriptResolver(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(), (Self::DynamicLibrary(a), Self::DynamicLibrary(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(), + (Self::Event(a), Self::Event(b)) => Arc::as_ptr(a).addr() == Arc::as_ptr(b).addr(), _ => false, } } @@ -337,3 +342,4 @@ ffi_obj_conversions!(Arc, DamageLibrary) ffi_obj_conversions!(Arc, MiscLibrary); ffi_obj_conversions!(Arc, ScriptResolver); ffi_obj_conversions!(Arc, DynamicLibrary); +ffi_obj_conversions!(Arc, Event); diff --git a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs index 5c4730b..b58aabe 100755 --- a/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs +++ b/src/script_implementations/wasm/export_registry/dynamic_data/pokemon.rs @@ -1,7 +1,7 @@ use std::mem::transmute; use crate::defines::LevelInt; -use crate::dynamic_data::{Battle, DynamicLibrary, LearnedMove, Pokemon, VolatileScriptsOwner}; +use crate::dynamic_data::{Battle, DynamicLibrary, EventBatchId, LearnedMove, Pokemon, VolatileScriptsOwner}; use crate::script_implementations::wasm::export_registry::{ get_value, get_value_arc, get_value_arc_void, get_value_call_getter, get_value_void, register, try_wasm, wasm_err, wasm_ok, WasmResult, WasmVoidResult, WasmVoidResultExtension, @@ -120,14 +120,16 @@ register! { env: FunctionEnvMut, pokemon: ExternRef, damage: u32, - source: u8 + source: u8, + event_batch_id_1: u64, + event_batch_id_2: u64, ) -> WasmVoidResult { unsafe { let pokemon = match pokemon.value_func(&env) { Ok(pokemon) => pokemon, Err(e) => return WasmVoidResult::err(e, &env), }; - match pokemon.damage(damage, transmute(source)) { + match pokemon.damage(damage, transmute(source), EventBatchId::from_u64_pair(event_batch_id_1, event_batch_id_2)) { Ok(()) => WasmVoidResult::ok(), Err(e) => WasmVoidResult::err(e, &env), } diff --git a/tests/data/gen7_scripts.wasm b/tests/data/gen7_scripts.wasm index e70de53..62c45c2 100755 Binary files a/tests/data/gen7_scripts.wasm and b/tests/data/gen7_scripts.wasm differ