Initial work on rune as scripting library
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2024-04-07 18:55:41 +02:00
parent 6379abf446
commit 67b0abe59f
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
24 changed files with 1186 additions and 739 deletions

View File

@ -16,7 +16,8 @@ path = "src/lib.rs"
ffi = [] ffi = []
serde = ["dep:serde", "dep:serde-xml-rs", "atomig/serde"] serde = ["dep:serde", "dep:serde-xml-rs", "atomig/serde"]
wasm = ["dep:wasmer"] wasm = ["dep:wasmer"]
default = ["serde", "wasm", "ffi"] rune = ["dep:rune"]
default = ["serde", "rune", "ffi"]
[profile.dev] [profile.dev]
opt-level = 0 opt-level = 0
@ -55,7 +56,6 @@ parking_lot = "0.12"
serde = { version = "1.0", optional = true, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] }
serde_repr = "0.1" serde_repr = "0.1"
serde-xml-rs = { version = "0.6", optional = true } serde-xml-rs = { version = "0.6", optional = true }
wasmer = { version = "4.2", optional = true, default-features = false, features = ["sys", "wat", "llvm"] }
uuid = "1.5" uuid = "1.5"
paste = { version = "1.0" } paste = { version = "1.0" }
arcstr = { version = "1.1", features = ["std"] } arcstr = { version = "1.1", features = ["std"] }
@ -65,6 +65,9 @@ anyhow_ext = "0.2"
thiserror = "1.0" thiserror = "1.0"
stdext = "0.3" stdext = "0.3"
wasmer = { version = "4.2", optional = true, default-features = false, features = ["sys", "wat", "llvm"] }
rune = { version = "0.13.2", optional = true }
[dev-dependencies] [dev-dependencies]
csv = "1.3" csv = "1.3"
project-root = "0.2" project-root = "0.2"
@ -73,4 +76,5 @@ serde_json = "1.0"
serde_plain = "1.0" serde_plain = "1.0"
# Allow us to assert whether floats are approximately a value # Allow us to assert whether floats are approximately a value
assert_approx_eq = "1.1" assert_approx_eq = "1.1"
mockall = "0.11" mockall = "0.12"
walkdir = "2.3"

View File

@ -1 +1,2 @@
max_width = 120 max_width = 120
fn_single_line = true

View File

@ -47,7 +47,7 @@ impl Gen7MiscLibrary {
Some(Arc::new(SecondaryEffectImpl::new( Some(Arc::new(SecondaryEffectImpl::new(
-1.0, -1.0,
StringKey::new("struggle"), StringKey::new("struggle"),
vec![], Default::default(),
))), ))),
HashSet::new(), HashSet::new(),
)); ));

View File

@ -33,59 +33,34 @@ pub struct HitData {
impl HitData { impl HitData {
/// Whether or not the hit is critical. /// Whether or not the hit is critical.
pub fn is_critical(&self) -> bool { pub fn is_critical(&self) -> bool { self.critical.load(Ordering::Relaxed) }
self.critical.load(Ordering::Relaxed)
}
/// The base power of the hit. /// The base power of the hit.
pub fn base_power(&self) -> u8 { pub fn base_power(&self) -> u8 { self.base_power.load(Ordering::Relaxed) }
self.base_power.load(Ordering::Relaxed)
}
/// The type effectiveness of the hit. /// The type effectiveness of the hit.
pub fn effectiveness(&self) -> f32 { pub fn effectiveness(&self) -> f32 { self.effectiveness.load(Ordering::Relaxed) }
self.effectiveness.load(Ordering::Relaxed)
}
/// The actual damage of the hit. /// The actual damage of the hit.
pub fn damage(&self) -> u32 { pub fn damage(&self) -> u32 { self.damage.load(Ordering::Relaxed) }
self.damage.load(Ordering::Relaxed)
}
/// The type id of the type used for the hit. /// The type id of the type used for the hit.
pub fn move_type(&self) -> TypeIdentifier { pub fn move_type(&self) -> TypeIdentifier { self.move_type.load(Ordering::Relaxed) }
self.move_type.load(Ordering::Relaxed)
}
/// Whether or not the hit has failed. /// Whether or not the hit has failed.
pub fn has_failed(&self) -> bool { pub fn has_failed(&self) -> bool { self.has_failed.load(Ordering::Relaxed) }
self.has_failed.load(Ordering::Relaxed)
}
/// Sets whether or not the hit is critical. /// Sets whether or not the hit is critical.
pub fn set_critical(&self, value: bool) { pub fn set_critical(&self, value: bool) { self.critical.store(value, Ordering::SeqCst); }
self.critical.store(value, Ordering::SeqCst);
}
/// Sets the base power of the hit. /// Sets the base power of the hit.
pub fn set_base_power(&self, value: u8) { pub fn set_base_power(&self, value: u8) { self.base_power.store(value, Ordering::SeqCst); }
self.base_power.store(value, Ordering::SeqCst);
}
/// Sets the type effectiveness of the hit. /// Sets the type effectiveness of the hit.
pub fn set_effectiveness(&self, value: f32) { pub fn set_effectiveness(&self, value: f32) { self.effectiveness.store(value, Ordering::SeqCst); }
self.effectiveness.store(value, Ordering::SeqCst);
}
/// Sets the actual damage of the hit. /// Sets the actual damage of the hit.
pub fn set_damage(&self, value: u32) { pub fn set_damage(&self, value: u32) { self.damage.store(value, Ordering::SeqCst); }
self.damage.store(value, Ordering::SeqCst);
}
/// Sets the move type id of the hit. /// Sets the move type id of the hit.
pub fn set_move_type(&self, value: TypeIdentifier) { pub fn set_move_type(&self, value: TypeIdentifier) { self.move_type.store(value, Ordering::SeqCst); }
self.move_type.store(value, Ordering::SeqCst);
}
/// Marks the hit as failed. /// Marks the hit as failed.
pub fn fail(&self) { pub fn fail(&self) { self.has_failed.store(true, Ordering::SeqCst); }
self.has_failed.store(true, Ordering::SeqCst);
}
} }
/// An executing move is the data of the move for while it is executing. /// An executing move is the data of the move for while it is executing.
#[derive(Debug)] #[derive(Debug)]
pub struct ExecutingMove { pub struct ExecutingMove {
/// The number of hits this move has. /// The number of hits this move has.
number_of_hits: u8, number_of_hits: u8,
@ -134,29 +109,17 @@ impl ExecutingMove {
} }
/// The number of targets this move has. /// The number of targets this move has.
pub fn target_count(&self) -> usize { pub fn target_count(&self) -> usize { self.targets.len() }
self.targets.len()
}
/// The number of hits this move has per target. /// The number of hits this move has per target.
pub fn number_of_hits(&self) -> u8 { pub fn number_of_hits(&self) -> u8 { self.number_of_hits }
self.number_of_hits
}
/// The user of the move. /// The user of the move.
pub fn user(&self) -> &Pokemon { pub fn user(&self) -> &Pokemon { &self.user }
&self.user
}
/// The move the user has actually chosen to do. /// The move the user has actually chosen to do.
pub fn chosen_move(&self) -> &Arc<LearnedMove> { pub fn chosen_move(&self) -> &Arc<LearnedMove> { &self.chosen_move }
&self.chosen_move
}
/// The move that the user is actually going to do. /// The move that the user is actually going to do.
pub fn use_move(&self) -> &Arc<dyn MoveData> { pub fn use_move(&self) -> &Arc<dyn MoveData> { &self.use_move }
&self.use_move
}
/// The script of the move. /// The script of the move.
pub fn script(&self) -> &ScriptContainer { pub fn script(&self) -> &ScriptContainer { &self.script }
&self.script
}
/// Gets a hit data for a target, with a specific index. /// Gets a hit data for a target, with a specific index.
pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> Result<&Arc<HitData>> { pub fn get_hit_data(&self, for_target: &Pokemon, hit: u8) -> Result<&Arc<HitData>> {
@ -215,17 +178,11 @@ impl ExecutingMove {
} }
impl ScriptSource for ExecutingMove { impl ScriptSource for ExecutingMove {
fn get_script_count(&self) -> Result<usize> { fn get_script_count(&self) -> Result<usize> { Ok(1) }
Ok(1)
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.script_source_data }
&self.script_source_data
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { scripts.push((&self.script).into()); }
scripts.push((&self.script).into());
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> { fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts); self.get_own_scripts(scripts);

View File

@ -216,21 +216,13 @@ impl Pokemon {
} }
/// The library data of the Pokemon. /// The library data of the Pokemon.
pub fn library(&self) -> &Arc<dyn DynamicLibrary> { pub fn library(&self) -> &Arc<dyn DynamicLibrary> { &self.data.library }
&self.data.library
}
/// The species of the Pokemon. /// The species of the Pokemon.
pub fn species(&self) -> Arc<dyn Species> { pub fn species(&self) -> Arc<dyn Species> { self.data.species.read().clone() }
self.data.species.read().clone()
}
/// The form of the Pokemon. /// The form of the 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. /// Whether or not the Pokemon is showing as a different species than it actually is.
pub fn has_different_display_species(&self) -> bool { pub fn has_different_display_species(&self) -> bool { self.data.display_species.is_some() }
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 {
@ -240,9 +232,7 @@ impl Pokemon {
} }
} }
/// Whether or not the Pokemon is showing as a different form than it actually is. /// Whether or not the Pokemon is showing as a different form than it actually is.
pub fn has_different_display_form(&self) -> bool { pub fn has_different_display_form(&self) -> bool { self.data.display_form.is_some() }
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 {
@ -253,32 +243,20 @@ impl Pokemon {
} }
/// The current level of the Pokemon. /// The current level of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Level) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Level)
pub fn level(&self) -> LevelInt { pub fn level(&self) -> LevelInt { self.data.level.load(Ordering::Relaxed) }
self.data.level.load(Ordering::Relaxed)
}
/// The amount of experience of the Pokemon. /// The amount of experience of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Experience) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Experience)
pub fn experience(&self) -> u32 { pub fn experience(&self) -> u32 { self.data.experience.load(Ordering::Relaxed) }
self.data.experience.load(Ordering::Relaxed)
}
/// The personality value of the Pokemon. /// The personality value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Personality_value) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Personality_value)
pub fn personality_value(&self) -> u32 { pub fn personality_value(&self) -> u32 { self.data.personality_value }
self.data.personality_value
}
/// The gender of the Pokemon. /// The gender of the Pokemon.
pub fn gender(&self) -> Gender { pub fn gender(&self) -> Gender { *self.data.gender.read() }
*self.data.gender.read()
}
/// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are /// The coloring of the Pokemon. Value 0 is the default, value 1 means shiny. Other values are
/// currently not used, and can be used for other implementations. /// currently not used, and can be used for other implementations.
pub fn coloring(&self) -> u8 { pub fn coloring(&self) -> u8 { self.data.coloring }
self.data.coloring
}
/// Gets the held item of a Pokemon /// Gets the held item of a Pokemon
pub fn held_item(&self) -> &RwLock<Option<Arc<dyn Item>>> { pub fn held_item(&self) -> &RwLock<Option<Arc<dyn Item>>> { &self.data.held_item }
&self.data.held_item
}
/// Checks whether the Pokemon is holding a specific item. /// Checks whether the Pokemon is holding a specific item.
pub fn has_held_item(&self, name: &StringKey) -> bool { pub fn has_held_item(&self, name: &StringKey) -> bool {
// Only true if we have an item, and the item name is the same as the requested item. // Only true if we have an item, and the item name is the same as the requested item.
@ -292,9 +270,7 @@ impl Pokemon {
self.data.held_item.write().replace(item.clone()) self.data.held_item.write().replace(item.clone())
} }
/// Removes the held item from the Pokemon. Returns the previously held item. /// Removes the held item from the Pokemon. Returns the previously held item.
pub fn remove_held_item(&self) -> Option<Arc<dyn Item>> { pub fn remove_held_item(&self) -> Option<Arc<dyn Item>> { self.data.held_item.write().take() }
self.data.held_item.write().take()
}
/// Makes the Pokemon uses its held item. /// Makes the Pokemon uses its held item.
pub fn consume_held_item(&self) -> Result<bool> { pub fn consume_held_item(&self) -> Result<bool> {
if self.data.held_item.read().is_none() { if self.data.held_item.read().is_none() {
@ -316,72 +292,42 @@ impl Pokemon {
} }
/// The remaining health points of the Pokemon. /// The remaining health points of the Pokemon.
pub fn current_health(&self) -> u32 { pub fn current_health(&self) -> u32 { self.data.current_health.load(Ordering::Relaxed) }
self.data.current_health.load(Ordering::Relaxed)
}
/// The max health points of the Pokemon. /// The max health points of the Pokemon.
pub fn max_health(&self) -> u32 { pub fn max_health(&self) -> u32 { self.data.boosted_stats.hp() }
self.data.boosted_stats.hp()
}
/// The weight of the Pokemon in kilograms. /// The weight of the Pokemon in kilograms.
pub fn weight(&self) -> f32 { pub fn weight(&self) -> f32 { self.data.weight.load(Ordering::Relaxed) }
self.data.weight.load(Ordering::Relaxed)
}
/// Sets the weight of the Pokemon in kilograms. /// Sets the weight of the Pokemon in kilograms.
pub fn set_weight(&self, weight: f32) { pub fn set_weight(&self, weight: f32) { self.data.weight.store(weight, Ordering::Relaxed) }
self.data.weight.store(weight, Ordering::Relaxed)
}
/// The height of the Pokemon in meters. /// The height of the Pokemon in meters.
pub fn height(&self) -> f32 { pub fn height(&self) -> f32 { self.data.height.load(Ordering::Relaxed) }
self.data.height.load(Ordering::Relaxed)
}
/// The current happiness of the Pokemon. Also known as friendship. /// The current happiness of the Pokemon. Also known as friendship.
pub fn happiness(&self) -> u8 { pub fn happiness(&self) -> u8 { self.data.happiness.load(Ordering::Relaxed) }
self.data.happiness.load(Ordering::Relaxed)
}
/// An optional nickname of the Pokemon. /// An optional nickname of the Pokemon.
pub fn nickname(&self) -> &Option<String> { pub fn nickname(&self) -> &Option<String> { &self.data.nickname }
&self.data.nickname
}
/// An index of the ability to find the actual ability on the form. /// An index of the ability to find the actual ability on the form.
pub fn real_ability(&self) -> &AbilityIndex { pub fn real_ability(&self) -> &AbilityIndex { &self.data.ability_index }
&self.data.ability_index
}
/// The current types of the Pokemon. /// The current types of the Pokemon.
pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<TypeIdentifier>> { pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<TypeIdentifier>> { self.data.types.read() }
self.data.types.read()
}
/// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots /// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots
/// are defined by None. /// are defined by None.
pub fn learned_moves(&self) -> &RwLock<[Option<Arc<LearnedMove>>; MAX_MOVES]> { pub fn learned_moves(&self) -> &RwLock<[Option<Arc<LearnedMove>>; MAX_MOVES]> { &self.data.moves }
&self.data.moves
}
/// The stats of the Pokemon when disregarding any stat boosts. /// The stats of the Pokemon when disregarding any stat boosts.
pub fn flat_stats(&self) -> &Arc<StatisticSet<u32>> { pub fn flat_stats(&self) -> &Arc<StatisticSet<u32>> { &self.data.flat_stats }
&self.data.flat_stats
}
/// The amount of boosts on a specific stat. /// The amount of boosts on a specific stat.
pub fn stat_boosts(&self) -> &Arc<ClampedStatisticSet<i8, -6, 6>> { pub fn stat_boosts(&self) -> &Arc<ClampedStatisticSet<i8, -6, 6>> { &self.data.stat_boost }
&self.data.stat_boost
}
/// Whether or not this Pokemon is still an egg, and therefore cannot battle. /// Whether or not this Pokemon is still an egg, and therefore cannot battle.
pub fn is_egg(&self) -> bool { pub fn is_egg(&self) -> bool { self.data.is_egg }
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
}
/// Get the stat boosts for a specific stat. /// Get the stat boosts for a specific stat.
pub fn stat_boost(&self, stat: Statistic) -> i8 { pub fn stat_boost(&self, stat: Statistic) -> i8 { self.data.stat_boost.get_stat(stat) }
self.data.stat_boost.get_stat(stat)
}
/// Change a boosted stat by a certain amount. /// Change a boosted stat by a certain amount.
pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> Result<bool> { pub fn change_stat_boost(&self, stat: Statistic, mut diff_amount: i8, self_inflicted: bool) -> Result<bool> {
let mut prevent = false; let mut prevent = false;
@ -440,14 +386,10 @@ impl Pokemon {
/// Gets an individual value of the Pokemon. /// Gets an individual value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Individual_values) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Individual_values)
pub fn individual_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 31>> { pub fn individual_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 31>> { &self.data.individual_values }
&self.data.individual_values
}
/// Gets an effort value of the Pokemon. /// Gets an effort value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Effort_values) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Effort_values)
pub fn effort_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 252>> { pub fn effort_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 252>> { &self.data.effort_values }
&self.data.effort_values
}
/// Gets the battle the battle is currently in. /// Gets the battle the battle is currently in.
pub fn get_battle(&self) -> Option<Battle> { pub fn get_battle(&self) -> Option<Battle> {
@ -469,13 +411,9 @@ impl Pokemon {
} }
/// Get the index of the slot on the side of the battle the Pokemon is in. Only returns a value /// Get the index of the slot on the side of the battle the Pokemon is in. Only returns a value
/// if the Pokemon is on the battlefield. /// if the Pokemon is on the battlefield.
pub fn get_battle_index(&self) -> Option<u8> { pub fn get_battle_index(&self) -> Option<u8> { self.data.battle_data.read().as_ref().map(|data| data.index()) }
self.data.battle_data.read().as_ref().map(|data| data.index())
}
/// Returns whether something overrides the ability. /// Returns whether something overrides the ability.
pub fn is_ability_overriden(&self) -> bool { pub fn is_ability_overriden(&self) -> bool { self.data.override_ability.is_some() }
self.data.override_ability.is_some()
}
/// Returns the currently active ability. /// Returns the currently active ability.
pub fn active_ability(&self) -> Result<Arc<dyn Ability>> { pub fn active_ability(&self) -> Result<Arc<dyn Ability>> {
if let Some(v) = &self.data.override_ability { if let Some(v) = &self.data.override_ability {
@ -496,25 +434,17 @@ impl Pokemon {
} }
/// The script for the status. /// The script for the status.
pub fn status(&self) -> &ScriptContainer { pub fn status(&self) -> &ScriptContainer { &self.data.status_script }
&self.data.status_script
}
/// Returns the script for the currently active ability. /// Returns the script for the currently active ability.
pub fn ability_script(&self) -> &ScriptContainer { pub fn ability_script(&self) -> &ScriptContainer { &self.data.ability_script }
&self.data.ability_script
}
/// Whether or not the Pokemon is allowed to gain experience. /// Whether or not the Pokemon is allowed to gain experience.
pub fn allowed_experience_gain(&self) -> bool { pub fn allowed_experience_gain(&self) -> bool { self.data.allowed_experience }
self.data.allowed_experience
}
/// The nature of the Pokemon. /// The nature of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Nature) /// [See also](https://bulbapedia.bulbagarden.net/wiki/Nature)
pub fn nature(&self) -> &Arc<dyn Nature> { pub fn nature(&self) -> &Arc<dyn Nature> { &self.data.nature }
&self.data.nature
}
/// Calculates the flat stats on the Pokemon. This should be called when for example the base /// Calculates the flat stats on the Pokemon. This should be called when for example the base
/// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted /// stats, level, nature, IV, or EV changes. This has a side effect of recalculating the boosted
@ -606,7 +536,7 @@ impl Pokemon {
.set(ability_script) .set(ability_script)
.as_ref() .as_ref()
// Ensure the ability script gets initialized with the parameters for the ability. // Ensure the ability script gets initialized with the parameters for the ability.
.on_initialize(&self.data.library, ability.parameters().to_vec()); .on_initialize(&self.data.library, ability.parameters());
match script_result { match script_result {
Ok(_) => (), Ok(_) => (),
Err(e) => { Err(e) => {
@ -646,14 +576,10 @@ impl Pokemon {
} }
/// Whether or not the Pokemon is useable in a battle. /// Whether or not the Pokemon is useable in a battle.
pub fn is_usable(&self) -> bool { pub fn is_usable(&self) -> bool { !self.data.is_caught && !self.data.is_egg && !self.is_fainted() }
!self.data.is_caught && !self.data.is_egg && !self.is_fainted()
}
/// Returns whether the Pokemon is fainted. /// Returns whether the Pokemon is fainted.
pub fn is_fainted(&self) -> bool { pub fn is_fainted(&self) -> bool { self.current_health() == 0 }
self.current_health() == 0
}
/// Sets the current battle the Pokemon is in. /// Sets the current battle the Pokemon is in.
pub fn set_battle_data(&self, battle: WeakBattleReference, battle_side_index: u8) { pub fn set_battle_data(&self, battle: WeakBattleReference, battle_side_index: u8) {
@ -836,9 +762,7 @@ impl Pokemon {
} }
/// Removes the current non-volatile status from the Pokemon. /// Removes the current non-volatile status from the Pokemon.
pub fn clear_status(&self) { pub fn clear_status(&self) { self.data.status_script.clear() }
self.data.status_script.clear()
}
/// Increases the level by a certain amount /// Increases the level by a certain amount
pub fn change_level_by(&self, amount: LevelInt) -> Result<()> { pub fn change_level_by(&self, amount: LevelInt) -> Result<()> {
@ -858,9 +782,7 @@ impl Pokemon {
/// Converts the Pokemon into a serializable form. /// Converts the Pokemon into a serializable form.
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
pub fn serialize(&self) -> Result<super::serialization::SerializedPokemon> { pub fn serialize(&self) -> Result<super::serialization::SerializedPokemon> { self.into() }
self.into()
}
/// Deserializes a Pokemon from a serializable form. /// Deserializes a Pokemon from a serializable form.
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -995,23 +917,17 @@ impl Pokemon {
} }
/// Gets the inner pointer to the reference counted data. /// Gets the inner pointer to the reference counted data.
pub fn as_ptr(&self) -> *const c_void { pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void }
Arc::as_ptr(&self.data) as *const c_void
}
} }
impl PartialEq for Pokemon { impl PartialEq for Pokemon {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.data, &other.data) }
Arc::ptr_eq(&self.data, &other.data)
}
} }
impl Eq for Pokemon {} impl Eq for Pokemon {}
impl PartialEq for WeakPokemonReference { impl PartialEq for WeakPokemonReference {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool { Weak::ptr_eq(&self.data, &other.data) }
Weak::ptr_eq(&self.data, &other.data)
}
} }
impl Eq for WeakPokemonReference {} impl Eq for WeakPokemonReference {}
@ -1025,9 +941,7 @@ impl WeakPokemonReference {
} }
/// Gets the pointer to the underlying data. /// Gets the pointer to the underlying data.
pub(crate) fn as_ptr(&self) -> *const c_void { pub(crate) fn as_ptr(&self) -> *const c_void { self.data.as_ptr() as *const c_void }
self.data.as_ptr() as *const c_void
}
} }
/// The data of the Pokemon related to being in a battle. /// The data of the Pokemon related to being in a battle.
@ -1047,26 +961,16 @@ pub struct PokemonBattleData {
impl PokemonBattleData { impl PokemonBattleData {
/// The battle data of the Pokemon /// The battle data of the Pokemon
pub fn battle(&self) -> Option<Battle> { pub fn battle(&self) -> Option<Battle> { self.battle.upgrade() }
self.battle.upgrade()
}
/// The index of the side of the Pokemon /// The index of the side of the Pokemon
pub fn battle_side_index(&self) -> u8 { pub fn battle_side_index(&self) -> u8 { self.battle_side_index.load(Ordering::Relaxed) }
self.battle_side_index.load(Ordering::Relaxed)
}
/// The index of the slot on the side of the Pokemon. /// The index of the slot on the side of the Pokemon.
pub fn index(&self) -> u8 { pub fn index(&self) -> u8 { self.index.load(Ordering::Relaxed) }
self.index.load(Ordering::Relaxed)
}
/// Whether or not the Pokemon is on the battlefield. /// Whether or not the Pokemon is on the battlefield.
pub fn on_battle_field(&self) -> bool { pub fn on_battle_field(&self) -> bool { self.on_battle_field.load(Ordering::Relaxed) }
self.on_battle_field.load(Ordering::Relaxed)
}
/// A list of opponents the Pokemon has seen this battle. /// A list of opponents the Pokemon has seen this battle.
pub fn seen_opponents(&self) -> &RwLock<Vec<WeakPokemonReference>> { pub fn seen_opponents(&self) -> &RwLock<Vec<WeakPokemonReference>> { &self.seen_opponents }
&self.seen_opponents
}
} }
impl ScriptSource for Pokemon { impl ScriptSource for Pokemon {
@ -1083,9 +987,7 @@ impl ScriptSource for Pokemon {
Ok(c) Ok(c)
} }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.data.script_source_data }
&self.data.script_source_data
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.data.held_item_trigger_script).into()); scripts.push((&self.data.held_item_trigger_script).into());
@ -1109,9 +1011,7 @@ impl ScriptSource for Pokemon {
} }
impl VolatileScriptsOwner for Pokemon { impl VolatileScriptsOwner for Pokemon {
fn volatile_scripts(&self) -> &Arc<ScriptSet> { fn volatile_scripts(&self) -> &Arc<ScriptSet> { &self.data.volatile }
&self.data.volatile
}
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> { fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
self.data.library.load_script(self.into(), ScriptCategory::Pokemon, key) self.data.library.load_script(self.into(), ScriptCategory::Pokemon, key)

View File

@ -1,4 +1,5 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use hashbrown::HashMap;
use std::any::Any; use std::any::Any;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::ops::Deref; use std::ops::Deref;
@ -30,97 +31,69 @@ pub trait Script: Send + Sync {
fn get_marked_for_deletion(&self) -> &AtomicBool; fn get_marked_for_deletion(&self) -> &AtomicBool;
/// This marks the script for deletion, which will dispose of it as soon as possible. /// This marks the script for deletion, which will dispose of it as soon as possible.
fn mark_for_deletion(&self) { fn mark_for_deletion(&self) { self.get_marked_for_deletion().store(true, Ordering::SeqCst); }
self.get_marked_for_deletion().store(true, Ordering::SeqCst);
}
/// Helper function to get the value of the marked for deletion bool. /// Helper function to get the value of the marked for deletion bool.
fn is_marked_for_deletion(&self) -> bool { fn is_marked_for_deletion(&self) -> bool { self.get_marked_for_deletion().load(Ordering::SeqCst) }
self.get_marked_for_deletion().load(Ordering::SeqCst)
}
/// A script can be suppressed by other scripts. If a script is suppressed by at least one script /// A script can be suppressed by other scripts. If a script is suppressed by at least one script
/// we will not execute its methods. This should return the number of suppressions on the script. /// we will not execute its methods. This should return the number of suppressions on the script.
fn get_suppressed_count(&self) -> &AtomicUsize; fn get_suppressed_count(&self) -> &AtomicUsize;
/// Helper function to check if there is at least one suppression on the script /// Helper function to check if there is at least one suppression on the script
fn is_suppressed(&self) -> bool { fn is_suppressed(&self) -> bool { self.get_suppressed_count().load(Ordering::SeqCst) > 0 }
self.get_suppressed_count().load(Ordering::SeqCst) > 0
}
/// Adds a suppression. This makes the script not run anymore. Note that adding this should also /// Adds a suppression. This makes the script not run anymore. Note that adding this should also
/// remove the suppression later. /// remove the suppression later.
/// ///
/// A common pattern for this is to run this in the [`Self::on_initialize`] function, and run the /// A common pattern for this is to run this in the [`Self::on_initialize`] function, and run the
/// remove in the [`Self::on_remove`] function. /// remove in the [`Self::on_remove`] function.
fn add_suppression(&self) { fn add_suppression(&self) { self.get_suppressed_count().fetch_add(1, Ordering::SeqCst); }
self.get_suppressed_count().fetch_add(1, Ordering::SeqCst);
}
/// Removes a suppression. This allows the script to run again (provided other scripts are not /// Removes a suppression. This allows the script to run again (provided other scripts are not
/// suppressing it). Note that running this should only occur if an add was run before. /// suppressing it). Note that running this should only occur if an add was run before.
fn remove_suppression(&self) { fn remove_suppression(&self) { self.get_suppressed_count().fetch_sub(1, Ordering::SeqCst); }
self.get_suppressed_count().fetch_sub(1, Ordering::SeqCst);
}
/// This function is ran when a volatile effect is added while that volatile effect already is /// This function is ran when a volatile effect is added while that volatile effect already is
/// in place. Instead of adding the volatile effect twice, it will execute this function instead. /// in place. Instead of adding the volatile effect twice, it will execute this function instead.
fn stack(&self) -> Result<()> { fn stack(&self) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is ran when this script stops being in effect, and is removed from its owner. /// This function is ran when this script stops being in effect, and is removed from its owner.
fn on_remove(&self) -> Result<()> { fn on_remove(&self) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is ran when this script starts being in effect. /// This function is ran when this script starts being in effect.
fn on_initialize(&self, _library: &Arc<dyn DynamicLibrary>, _pars: Vec<Arc<Parameter>>) -> Result<()> { fn on_initialize(
&self,
_library: &Arc<dyn DynamicLibrary>,
_pars: &HashMap<StringKey, Arc<Parameter>>,
) -> Result<()> {
Ok(()) Ok(())
} }
/// This function is ran just before the start of the turn. Everyone has made its choices here, /// This function is ran just before the start of the turn. Everyone has made its choices here,
/// and the turn is about to start. This is a great place to initialize data if you need to know /// and the turn is about to start. This is a great place to initialize data if you need to know
/// something has happened during a turn. /// something has happened during a turn.
fn on_before_turn(&self, _choice: &Arc<TurnChoice>) -> Result<()> { fn on_before_turn(&self, _choice: &Arc<TurnChoice>) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows you to modify the effective speed of the Pokemon. This is ran before /// This function allows you to modify the effective speed of the Pokemon. This is ran before
/// turn ordering, so overriding here will allow you to put certain Pokemon before others. /// turn ordering, so overriding here will allow you to put certain Pokemon before others.
fn change_speed(&self, _choice: &Arc<TurnChoice>, _speed: &mut u32) -> Result<()> { fn change_speed(&self, _choice: &Arc<TurnChoice>, _speed: &mut u32) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows you to modify the effective priority of the Pokemon. This is ran before /// This function allows you to modify the effective priority of the Pokemon. This is ran before
/// turn ordering, so overriding here will allow you to put certain Pokemon before others. Note /// turn ordering, so overriding here will allow you to put certain Pokemon before others. Note
/// that this is only relevant on move choices, as other turn choice types do not have a priority. /// that this is only relevant on move choices, as other turn choice types do not have a priority.
fn change_priority(&self, _choice: &Arc<TurnChoice>, _priority: &mut i8) -> Result<()> { fn change_priority(&self, _choice: &Arc<TurnChoice>, _priority: &mut i8) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows you to change the move that is used during execution. This is useful for /// This function allows you to change the move that is used during execution. This is useful for
/// moves such as metronome, where the move chosen actually differs from the move used. /// moves such as metronome, where the move chosen actually differs from the move used.
fn change_move(&self, _choice: &Arc<TurnChoice>, _move_name: &mut StringKey) -> Result<()> { fn change_move(&self, _choice: &Arc<TurnChoice>, _move_name: &mut StringKey) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows you to change a move into a multi-hit move. The number of hits set here /// This function allows you to change a move into a multi-hit move. The number of hits set here
/// gets used as the number of hits. If set to 0, this will behave as if the move missed on its /// gets used as the number of hits. If set to 0, this will behave as if the move missed on its
/// first hit. /// first hit.
fn change_number_of_hits(&self, _choice: &Arc<TurnChoice>, _number_of_hits: &mut u8) -> Result<()> { fn change_number_of_hits(&self, _choice: &Arc<TurnChoice>, _number_of_hits: &mut u8) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows you to prevent a move from running. If this gets set to true, the move /// This function allows you to prevent a move from running. If this gets set to true, the move
/// ends execution here. No PP will be decreased in this case. /// ends execution here. No PP will be decreased in this case.
fn prevent_move(&self, _move: &Arc<ExecutingMove>, _prevent: &mut bool) -> Result<()> { fn prevent_move(&self, _move: &Arc<ExecutingMove>, _prevent: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function makes the move fail. If the fail field gets set to true, the move ends execution, /// This function makes the move fail. If the fail field gets set to true, the move ends execution,
/// and fail events get triggered. /// and fail events get triggered.
fn fail_move(&self, _move: &Arc<ExecutingMove>, _fail: &mut bool) -> Result<()> { fn fail_move(&self, _move: &Arc<ExecutingMove>, _fail: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be /// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be
/// decreased. /// decreased.
fn stop_before_move(&self, _move: &Arc<ExecutingMove>, _stop: &mut bool) -> Result<()> { fn stop_before_move(&self, _move: &Arc<ExecutingMove>, _stop: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function runs just before the move starts its execution. /// This function runs just before the move starts its execution.
fn on_before_move(&self, _move: &Arc<ExecutingMove>) -> Result<()> { fn on_before_move(&self, _move: &Arc<ExecutingMove>) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows a script to prevent a move that is targeted at its owner. If set to true /// This function allows a script to prevent a move that is targeted at its owner. If set to true
/// the move fails, and fail events get triggered. /// the move fails, and fail events get triggered.
fn fail_incoming_move(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _fail: &mut bool) -> Result<()> { fn fail_incoming_move(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _fail: &mut bool) -> Result<()> {
@ -132,9 +105,7 @@ pub trait Script: Send + Sync {
} }
/// This function occurs when a move gets missed. This runs on the scripts belonging to the executing /// This function occurs when a move gets missed. This runs on the scripts belonging to the executing
/// move, which include the scripts that are attached to the owner of the script. /// move, which include the scripts that are attached to the owner of the script.
fn on_move_miss(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { fn on_move_miss(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows the script to change the actual type that is used for the move on a target. /// This function allows the script to change the actual type that is used for the move on a target.
fn change_move_type( fn change_move_type(
&self, &self,
@ -308,13 +279,9 @@ pub trait Script: Send + Sync {
} }
/// This function triggers when an incoming hit happens. This triggers after the damage is done, /// This function triggers when an incoming hit happens. This triggers after the damage is done,
/// but before the secondary effect of the move happens. /// but before the secondary effect of the move happens.
fn on_incoming_hit(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { fn on_incoming_hit(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
Ok(())
}
/// This function triggers when an opponent on the field faints. /// This function triggers when an opponent on the field faints.
fn on_opponent_faints(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { fn on_opponent_faints(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows a script attached to a Pokemon or its parents to prevent stat boost /// This function allows a script attached to a Pokemon or its parents to prevent stat boost
/// changes on that Pokemon. /// changes on that Pokemon.
fn prevent_stat_boost_change( fn prevent_stat_boost_change(
@ -380,61 +347,37 @@ pub trait Script: Send + Sync {
/// This function triggers when the move uses its secondary effect. Moves should implement their /// This function triggers when the move uses its secondary effect. Moves should implement their
/// secondary effects here. Status moves should implement their actual functionality in this /// secondary effects here. Status moves should implement their actual functionality in this
/// function as well, as status moves effects are defined as secondary effects for simplicity. /// function as well, as status moves effects are defined as secondary effects for simplicity.
fn on_secondary_effect(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { fn on_secondary_effect(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
Ok(())
}
/// This function triggers on a move or its parents when all hits on a target are finished. /// This function triggers on a move or its parents when all hits on a target are finished.
fn on_after_hits(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { fn on_after_hits(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { Ok(()) }
Ok(())
}
/// This function prevents the Pokemon it is attached to from being able to switch out. /// This function prevents the Pokemon it is attached to from being able to switch out.
fn prevent_self_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { fn prevent_self_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows the prevention of switching for any opponent. /// This function allows the prevention of switching for any opponent.
fn prevent_opponent_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { fn prevent_opponent_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is called on a move and its parents when the move fails. /// This function is called on a move and its parents when the move fails.
fn on_fail(&self, _target: &Pokemon) -> Result<()> { fn on_fail(&self, _target: &Pokemon) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is called on a script when an opponent fails. /// This function is called on a script when an opponent fails.
fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> { fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> { Ok(()) }
Ok(())
}
/// This function allows preventing the running away of the Pokemon its attached to /// This function allows preventing the running away of the Pokemon its attached to
fn prevent_self_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { fn prevent_self_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function prevents a Pokemon on another side than where its attached to from running away. /// This function prevents a Pokemon on another side than where its attached to from running away.
fn prevent_opponent_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { fn prevent_opponent_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function id triggered on all scripts active in the battle after all choices have finished /// This function id triggered on all scripts active in the battle after all choices have finished
/// running. Note that choices are not active anymore here, so their scripts do not call this /// running. Note that choices are not active anymore here, so their scripts do not call this
/// function. /// function.
fn on_end_turn(&self) -> Result<()> { fn on_end_turn(&self) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage. /// This function is triggered on a Pokemon and its parents when the given Pokemon takes damage.
fn on_damage(&self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) -> Result<()> { fn on_damage(&self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) -> Result<()> {
Ok(()) Ok(())
} }
/// This function is triggered on a Pokemon and its parents when the given Pokemon faints. /// This function is triggered on a Pokemon and its parents when the given Pokemon faints.
fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> { fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into /// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into
/// the battlefield. /// the battlefield.
fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> { fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the /// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the
/// held item it had. /// held item it had.
fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &Arc<dyn Item>) -> Result<()> { fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &Arc<dyn Item>) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience, /// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience,
/// and allows for changing this amount of experience. /// and allows for changing this amount of experience.
fn change_experience_gained( fn change_experience_gained(
@ -452,9 +395,7 @@ pub trait Script: Send + Sync {
} }
/// This function is triggered on a battle and its parents when something attempts to change the /// This function is triggered on a battle and its parents when something attempts to change the
/// weather, and allows for blocking the weather change. /// weather, and allows for blocking the weather change.
fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> { fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> { Ok(()) }
Ok(())
}
/// This function is called when a Pokeball is thrown at a Pokemon, and allows modifying the catch /// This function is called when a Pokeball is thrown at a Pokemon, and allows modifying the catch
/// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for /// rate of this attempt. Pokeball modifier effects should be implemented here, as well as for
/// example status effects that change capture rates. /// example status effects that change capture rates.
@ -474,9 +415,7 @@ pub trait Script: Send + Sync {
} }
impl Debug for dyn Script { impl Debug for dyn Script {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { Ok(()) }
Ok(())
}
} }
/// A script holder defines the underlying type of how we store individual scripts on a script source. /// A script holder defines the underlying type of how we store individual scripts on a script source.
@ -574,14 +513,10 @@ impl ScriptContainer {
} }
/// Gets the underlying reference counter to the script. /// Gets the underlying reference counter to the script.
pub fn arc(&self) -> &ScriptHolder { pub fn arc(&self) -> &ScriptHolder { &self.script }
&self.script
}
/// Whether or not the script is set. /// Whether or not the script is set.
pub fn is_any(&self) -> bool { pub fn is_any(&self) -> bool { self.script.read().is_some() }
self.script.read().is_some()
}
/// Get the underlying script as the downcasted value. /// Get the underlying script as the downcasted value.
pub fn get_as<T: 'static>(&self) -> Result<MappedRwLockReadGuard<T>> { pub fn get_as<T: 'static>(&self) -> Result<MappedRwLockReadGuard<T>> {
@ -646,29 +581,19 @@ mod tests {
unsafe impl Send for TestScript {} unsafe impl Send for TestScript {}
impl Script for TestScript { impl Script for TestScript {
fn name(&self) -> Result<&StringKey> { fn name(&self) -> Result<&StringKey> { Ok(&self.name) }
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool { fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion }
&self.marked_for_deletion
}
fn get_suppressed_count(&self) -> &AtomicUsize { fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
&self.suppressed_count
}
fn stack(&self) -> Result<()> { fn stack(&self) -> Result<()> {
unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() } unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() }
Ok(()) Ok(())
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any { self }
self fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
// Removing yourself while active should be completely valid for a script. Consider for example // Removing yourself while active should be completely valid for a script. Consider for example
@ -726,24 +651,14 @@ mod tests {
unsafe impl Send for ReplaceTestScript {} unsafe impl Send for ReplaceTestScript {}
impl Script for ReplaceTestScript { impl Script for ReplaceTestScript {
fn name(&self) -> Result<&StringKey> { fn name(&self) -> Result<&StringKey> { Ok(&self.name) }
Ok(&self.name)
}
fn get_marked_for_deletion(&self) -> &AtomicBool { fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion }
&self.marked_for_deletion
}
fn get_suppressed_count(&self) -> &AtomicUsize { fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
&self.suppressed_count
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any { self }
self fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
} }
#[test] #[test]
@ -793,19 +708,13 @@ pub enum ScriptOwnerData {
} }
impl From<&Pokemon> for ScriptOwnerData { impl From<&Pokemon> for ScriptOwnerData {
fn from(p: &Pokemon) -> Self { fn from(p: &Pokemon) -> Self { ScriptOwnerData::Pokemon(p.weak()) }
ScriptOwnerData::Pokemon(p.weak())
}
} }
impl From<&BattleSide> for ScriptOwnerData { impl From<&BattleSide> for ScriptOwnerData {
fn from(p: &BattleSide) -> Self { fn from(p: &BattleSide) -> Self { ScriptOwnerData::BattleSide(p.weak()) }
ScriptOwnerData::BattleSide(p.weak())
}
} }
impl From<&Battle> for ScriptOwnerData { impl From<&Battle> for ScriptOwnerData {
fn from(p: &Battle) -> Self { fn from(p: &Battle) -> Self { ScriptOwnerData::Battle(p.weak()) }
ScriptOwnerData::Battle(p.weak())
}
} }

View File

@ -1,9 +1,10 @@
use crate::ffi::FFIHandle;
use crate::ffi::FromFFIHandle; use crate::ffi::FromFFIHandle;
use crate::ffi::{FFIHandle, NonOwnedPtrString};
use crate::ffi::{FFIResult, OwnedPtrString}; use crate::ffi::{FFIResult, OwnedPtrString};
use crate::static_data::{Ability, AbilityImpl, Parameter}; use crate::static_data::{Ability, AbilityImpl, Parameter};
use crate::StringKey; use crate::StringKey;
use anyhow::anyhow; use anyhow::anyhow;
use hashbrown::HashMap;
use std::ffi::{c_char, CStr, CString}; use std::ffi::{c_char, CStr, CString};
use std::sync::Arc; use std::sync::Arc;
@ -12,13 +13,18 @@ use std::sync::Arc;
unsafe extern "C" fn ability_new( unsafe extern "C" fn ability_new(
name: *const c_char, name: *const c_char,
effect: *const c_char, effect: *const c_char,
parameter_keys: *const NonOwnedPtrString,
parameters: *const FFIHandle<Arc<Parameter>>, parameters: *const FFIHandle<Arc<Parameter>>,
parameters_length: usize, parameters_length: usize,
) -> FFIResult<FFIHandle<Arc<dyn Ability>>> { ) -> FFIResult<FFIHandle<Arc<dyn Ability>>> {
let parameter_keys = std::slice::from_raw_parts(parameter_keys, parameters_length);
let parameters = std::slice::from_raw_parts(parameters, parameters_length); let parameters = std::slice::from_raw_parts(parameters, parameters_length);
let mut parameters_vec: Vec<Arc<Parameter>> = Vec::with_capacity(parameters_length); let mut parameters_map: HashMap<StringKey, Arc<Parameter>> = HashMap::with_capacity(parameters_length);
for parameter in parameters { for (index, parameter) in parameters.iter().enumerate() {
parameters_vec.push(parameter.from_ffi_handle()); parameters_map.insert(
CStr::from_ptr(parameter_keys[index]).into(),
parameter.from_ffi_handle(),
);
} }
let name: StringKey = match CStr::from_ptr(name).to_str() { let name: StringKey = match CStr::from_ptr(name).to_str() {
@ -30,7 +36,7 @@ unsafe extern "C" fn ability_new(
Err(_) => return FFIResult::err(anyhow!("Failed to convert effect to CStr")), Err(_) => return FFIResult::err(anyhow!("Failed to convert effect to CStr")),
}; };
let arc: Arc<dyn Ability> = Arc::new(AbilityImpl::new(&name, &effect, parameters_vec)); let arc: Arc<dyn Ability> = Arc::new(AbilityImpl::new(&name, &effect, parameters_map));
FFIResult::ok(FFIHandle::get_handle(arc.into())) FFIResult::ok(FFIHandle::get_handle(arc.into()))
} }
@ -62,9 +68,10 @@ unsafe extern "C" fn ability_parameter_length(ptr: FFIHandle<Arc<dyn Ability>>)
#[no_mangle] #[no_mangle]
unsafe extern "C" fn ability_parameter_get( unsafe extern "C" fn ability_parameter_get(
ptr: FFIHandle<Arc<dyn Ability>>, ptr: FFIHandle<Arc<dyn Ability>>,
index: usize, name: NonOwnedPtrString,
) -> FFIHandle<Arc<Parameter>> { ) -> FFIHandle<Arc<Parameter>> {
if let Some(p) = ptr.from_ffi_handle().parameters().get(index) { let string: StringKey = CStr::from_ptr(name).into();
if let Some(p) = ptr.from_ffi_handle().parameters().get(&string) {
FFIHandle::get_handle(p.clone().into()) FFIHandle::get_handle(p.clone().into())
} else { } else {
FFIHandle::none() FFIHandle::none()

View File

@ -5,7 +5,7 @@ use crate::static_data::{
}; };
use crate::StringKey; use crate::StringKey;
use anyhow::anyhow; use anyhow::anyhow;
use hashbrown::HashSet; use hashbrown::{HashMap, HashSet};
use std::ffi::{c_char, CStr, CString}; use std::ffi::{c_char, CStr, CString};
use std::sync::Arc; use std::sync::Arc;
@ -99,13 +99,16 @@ unsafe extern "C" fn move_data_has_flag(ptr: FFIHandle<Arc<dyn MoveData>>, flag:
unsafe extern "C" fn secondary_effect_new( unsafe extern "C" fn secondary_effect_new(
chance: f32, chance: f32,
effect_name: NonOwnedPtrString, effect_name: NonOwnedPtrString,
parameter_keys: *const NonOwnedPtrString,
parameters: *mut FFIHandle<Arc<Parameter>>, parameters: *mut FFIHandle<Arc<Parameter>>,
parameters_length: usize, parameters_length: usize,
) -> FFIHandle<Box<dyn SecondaryEffect>> { ) -> FFIHandle<Box<dyn SecondaryEffect>> {
let parameter_key_slice = std::slice::from_raw_parts(parameter_keys, parameters_length);
let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length); let parameter_slice = std::slice::from_raw_parts(parameters, parameters_length);
let mut parameters = Vec::with_capacity(parameters_length); let mut parameters = HashMap::with_capacity(parameters_length);
for parameter in parameter_slice { for (index, parameter) in parameter_slice.iter().enumerate() {
parameters.push(parameter.from_ffi_handle()) let key = CStr::from_ptr(parameter_key_slice[index]).into();
parameters.insert(key, parameter.from_ffi_handle());
} }
let b: Arc<dyn SecondaryEffect> = Arc::new(SecondaryEffectImpl::new( let b: Arc<dyn SecondaryEffect> = Arc::new(SecondaryEffectImpl::new(
@ -146,9 +149,10 @@ unsafe extern "C" fn secondary_effect_parameter_length(ptr: FFIHandle<Arc<dyn Se
#[no_mangle] #[no_mangle]
unsafe extern "C" fn secondary_effect_parameter_get( unsafe extern "C" fn secondary_effect_parameter_get(
ptr: FFIHandle<Arc<dyn SecondaryEffect>>, ptr: FFIHandle<Arc<dyn SecondaryEffect>>,
index: usize, name: NonOwnedPtrString,
) -> FFIHandle<Arc<Parameter>> { ) -> FFIHandle<Arc<Parameter>> {
if let Some(v) = ptr.from_ffi_handle().parameters().get(index) { let string: StringKey = CStr::from_ptr(name).into();
if let Some(v) = ptr.from_ffi_handle().parameters().get(&string) {
FFIHandle::get_handle(v.clone().into()) FFIHandle::get_handle(v.clone().into())
} else { } else {
FFIHandle::none() FFIHandle::none()

View File

@ -9,8 +9,9 @@
#![allow(hidden_glob_reexports)] #![allow(hidden_glob_reexports)]
#![allow(clippy::arc_with_non_send_sync)] #![allow(clippy::arc_with_non_send_sync)]
// Documentation linters // Documentation linters
#![deny(missing_docs)] // FIXME: Enable these before committing
#![deny(clippy::missing_docs_in_private_items)] // #![deny(missing_docs)]
// #![deny(clippy::missing_docs_in_private_items)]
// Linter rules to prevent panics // Linter rules to prevent panics
// Currently still a WIP to fix all of these // Currently still a WIP to fix all of these
#![deny(clippy::unwrap_used)] #![deny(clippy::unwrap_used)]

View File

@ -1,3 +1,7 @@
/// The WASM module handles loading dynamic scripts through WebAssembly. /// The WASM module handles loading dynamic scripts through WebAssembly.
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
pub mod wasm; pub mod wasm;
/// The Rune module handles loading dynamic scripts through the Rune language.
#[cfg(feature = "rune")]
pub mod rune;

View File

@ -0,0 +1,134 @@
use crate::dynamic_data::{Battle, DamageSource, ExecutingMove, Pokemon, TurnChoice};
use crate::static_data::{Item, Statistic, TypeIdentifier};
use crate::StringKey;
use rune::Hash;
use std::sync::Arc;
mod script;
pub mod script_resolver;
pub(self) mod wrappers;
#[derive(Debug, Default)]
pub(self) struct RuneScriptType {
pub fn_on_initialize: Option<Hash>,
pub fn_on_stack: Option<Hash>,
pub fn_on_remove: Option<Hash>,
pub fn_on_before_turn: Option<Hash>,
pub fn_change_speed: Option<Hash>,
pub fn_change_priority: Option<Hash>,
pub fn_change_move: Option<Hash>,
pub fn_change_number_of_hits: Option<Hash>,
pub fn_prevent_move: Option<Hash>,
pub fn_fail_move: Option<Hash>,
pub fn_stop_before_move: Option<Hash>,
pub fn_on_before_move: Option<Hash>,
pub fn_fail_incoming_move: Option<Hash>,
pub fn_is_invulnerable: Option<Hash>,
pub fn_on_move_miss: Option<Hash>,
pub fn_change_move_type: Option<Hash>,
pub fn_change_effectiveness: Option<Hash>,
pub fn_block_critical: Option<Hash>,
pub fn_block_incoming_critical: Option<Hash>,
pub fn_change_accuracy: Option<Hash>,
pub fn_change_critical_stage: Option<Hash>,
pub fn_change_critical_modifier: Option<Hash>,
pub fn_change_stab_modifier: Option<Hash>,
pub fn_change_base_power: Option<Hash>,
pub fn_bypass_defensive_stat_boost: Option<Hash>,
pub fn_bypass_offensive_stat_boost: Option<Hash>,
pub fn_change_offensive_stat_value: Option<Hash>,
pub fn_change_defensive_stat_value: Option<Hash>,
pub fn_change_damage_stat_modifier: Option<Hash>,
pub fn_change_damage_modifier: Option<Hash>,
pub fn_change_damage: Option<Hash>,
pub fn_change_incoming_damage: Option<Hash>,
pub fn_on_incoming_hit: Option<Hash>,
pub fn_on_opponent_faints: Option<Hash>,
pub fn_prevent_stat_boost_change: Option<Hash>,
pub fn_change_stat_boost_change: Option<Hash>,
pub fn_prevent_secondary_effect: Option<Hash>,
pub fn_change_effect_chance: Option<Hash>,
pub fn_change_incoming_effect_chance: Option<Hash>,
pub fn_on_secondary_effect: Option<Hash>,
pub fn_on_after_hits: Option<Hash>,
pub fn_prevent_self_switch: Option<Hash>,
pub fn_prevent_opponent_switch: Option<Hash>,
pub fn_on_fail: Option<Hash>,
pub fn_on_opponent_fail: Option<Hash>,
pub fn_prevent_self_run_away: Option<Hash>,
pub fn_prevent_opponent_run_away: Option<Hash>,
pub fn_on_end_turn: Option<Hash>,
pub fn_on_damage: Option<Hash>,
pub fn_on_faint: Option<Hash>,
pub fn_on_switch_in: Option<Hash>,
pub fn_on_after_held_item_consume: Option<Hash>,
pub fn_change_experience_gained: Option<Hash>,
pub fn_share_experience: Option<Hash>,
pub fn_block_weather: Option<Hash>,
pub fn_change_capture_rate_bonus: Option<Hash>,
}
impl RuneScriptType {
pub fn on_found_fn(&mut self, fn_name: &str, hash: Hash) {
match fn_name {
"on_initialize" => self.fn_on_initialize = Some(hash),
"on_stack" => self.fn_on_stack = Some(hash),
"on_remove" => self.fn_on_remove = Some(hash),
"on_before_turn" => self.fn_on_before_turn = Some(hash),
"change_speed" => self.fn_change_speed = Some(hash),
"change_priority" => self.fn_change_priority = Some(hash),
"change_move" => self.fn_change_move = Some(hash),
"change_number_of_hits" => self.fn_change_number_of_hits = Some(hash),
"prevent_move" => self.fn_prevent_move = Some(hash),
"fail_move" => self.fn_fail_move = Some(hash),
"stop_before_move" => self.fn_stop_before_move = Some(hash),
"on_before_move" => self.fn_on_before_move = Some(hash),
"fail_incoming_move" => self.fn_fail_incoming_move = Some(hash),
"is_invulnerable" => self.fn_is_invulnerable = Some(hash),
"on_move_miss" => self.fn_on_move_miss = Some(hash),
"change_move_type" => self.fn_change_move_type = Some(hash),
"change_effectiveness" => self.fn_change_effectiveness = Some(hash),
"block_critical" => self.fn_block_critical = Some(hash),
"block_incoming_critical" => self.fn_block_incoming_critical = Some(hash),
"change_accuracy" => self.fn_change_accuracy = Some(hash),
"change_critical_stage" => self.fn_change_critical_stage = Some(hash),
"change_critical_modifier" => self.fn_change_critical_modifier = Some(hash),
"change_stab_modifier" => self.fn_change_stab_modifier = Some(hash),
"change_base_power" => self.fn_change_base_power = Some(hash),
"bypass_defensive_stat_boost" => self.fn_bypass_defensive_stat_boost = Some(hash),
"bypass_offensive_stat_boost" => self.fn_bypass_offensive_stat_boost = Some(hash),
"change_offensive_stat_value" => self.fn_change_offensive_stat_value = Some(hash),
"change_defensive_stat_value" => self.fn_change_defensive_stat_value = Some(hash),
"change_damage_stat_modifier" => self.fn_change_damage_stat_modifier = Some(hash),
"change_damage_modifier" => self.fn_change_damage_modifier = Some(hash),
"change_damage" => self.fn_change_damage = Some(hash),
"change_incoming_damage" => self.fn_change_incoming_damage = Some(hash),
"on_incoming_hit" => self.fn_on_incoming_hit = Some(hash),
"on_opponent_faints" => self.fn_on_opponent_faints = Some(hash),
"prevent_stat_boost_change" => self.fn_prevent_stat_boost_change = Some(hash),
"change_stat_boost_change" => self.fn_change_stat_boost_change = Some(hash),
"prevent_secondary_effect" => self.fn_prevent_secondary_effect = Some(hash),
"change_effect_chance" => self.fn_change_effect_chance = Some(hash),
"change_incoming_effect_chance" => self.fn_change_incoming_effect_chance = Some(hash),
"on_secondary_effect" => self.fn_on_secondary_effect = Some(hash),
"on_after_hits" => self.fn_on_after_hits = Some(hash),
"prevent_self_switch" => self.fn_prevent_self_switch = Some(hash),
"prevent_opponent_switch" => self.fn_prevent_opponent_switch = Some(hash),
"on_fail" => self.fn_on_fail = Some(hash),
"on_opponent_fail" => self.fn_on_opponent_fail = Some(hash),
"prevent_self_run_away" => self.fn_prevent_self_run_away = Some(hash),
"prevent_opponent_run_away" => self.fn_prevent_opponent_run_away = Some(hash),
"on_end_turn" => self.fn_on_end_turn = Some(hash),
"on_damage" => self.fn_on_damage = Some(hash),
"on_faint" => self.fn_on_faint = Some(hash),
"on_switch_in" => self.fn_on_switch_in = Some(hash),
"on_after_held_item_consume" => self.fn_on_after_held_item_consume = Some(hash),
"change_experience_gained" => self.fn_change_experience_gained = Some(hash),
"share_experience" => self.fn_share_experience = Some(hash),
"block_weather" => self.fn_block_weather = Some(hash),
"change_capture_rate_bonus" => self.fn_change_capture_rate_bonus = Some(hash),
_ => {}
}
}
}

View File

@ -0,0 +1,153 @@
use crate::dynamic_data::{DynamicLibrary, ExecutingMove, Pokemon, Script, ScriptOwnerData, TurnChoice};
use crate::script_implementations::rune::wrappers::*;
use crate::script_implementations::rune::RuneScriptType;
use crate::static_data::Parameter;
use crate::StringKey;
use hashbrown::HashMap;
use parking_lot::RwLock;
use rune::runtime::{Object, RuntimeContext, Shared, VmError, VmResult};
use rune::{Any, Unit, Value};
use std::convert::TryFrom;
use std::error::Error;
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::Arc;
pub struct RuneScript {
name: StringKey,
state: RwLock<Shared<Object>>,
/// Returns an atomic bool for internal marking of deletion. This is currently only specifically
/// used for deletion of a script while we are holding a reference to it (i.e. executing a script
/// hook on it).
marked_for_deletion: AtomicBool,
/// A script can be suppressed by other scripts. If a script is suppressed by at least one script
/// we will not execute its methods. This holds the number of suppressions on the script.
suppressed_count: AtomicUsize,
/// The owner of this script (where the script is attached to)
owner: ScriptOwnerData,
script_type: Arc<RuneScriptType>,
runtime: Arc<RuntimeContext>,
unit: Arc<Unit>,
}
unsafe impl Send for RuneScript {}
unsafe impl Sync for RuneScript {}
impl RuneScript {
pub fn new(
name: StringKey,
object: Shared<Object>,
owner: ScriptOwnerData,
script_type: Arc<RuneScriptType>,
runtime: Arc<RuntimeContext>,
unit: Arc<Unit>,
) -> Self {
Self {
name,
state: RwLock::new(object),
marked_for_deletion: Default::default(),
suppressed_count: Default::default(),
owner,
script_type,
runtime,
unit,
}
}
}
impl Script for RuneScript {
fn name(&self) -> anyhow::Result<&StringKey> { Ok(&self.name) }
fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion }
fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
fn on_initialize(
&self,
_: &Arc<dyn DynamicLibrary>,
pars: &HashMap<StringKey, Arc<Parameter>>,
) -> anyhow::Result<()> {
if pars.is_empty() {
return Ok(());
}
let mut write_lock = self.state.write();
for par in pars {
let key = rune::alloc::string::String::try_from(par.0.str())?;
write_lock
.borrow_mut()?
.insert(key, parameter_to_rune_value(par.1.as_ref())?)?;
}
Ok(())
}
fn block_critical(
&self,
move_data: &Arc<ExecutingMove>,
target: &Pokemon,
hit: u8,
block_critical: &mut bool,
) -> anyhow::Result<()> {
if let Some(hash) = self.script_type.fn_block_critical {
let mut vm = rune::runtime::Vm::new(self.runtime.clone(), self.unit.clone());
todo!()
// let block_critical_handle = RuneValueWrapper::new_mut(block_critical);
// let read_lock = self.state.read();
// let state = read_lock.deref();
//
// vm.execute(
// hash,
// vec![
// Value::Object(state.clone()),
// Value::from(move_data.wrap()?),
// Value::from(target.wrap()?),
// Value::from(hit),
// Value::from(block_critical_handle.clone().wrap()?),
// ],
// )?;
// *block_critical = block_critical_handle.value();
}
Ok(())
}
fn change_speed(&self, choice: &Arc<TurnChoice>, speed: &mut u32) -> anyhow::Result<()> {
if let Some(hash) = self.script_type.fn_change_speed {
let mut vm = rune::runtime::Vm::new(self.runtime.clone(), self.unit.clone());
let speed_handle = wrap_value_reference(*speed as i64)?;
let read_lock = self.state.read();
let state = read_lock.deref();
let res = vm
.execute(
hash,
vec![
Value::Object(state.clone()),
Value::from(choice.wrap()),
speed_handle.clone(),
],
)?
.complete();
if let VmResult::Err(e) = res {
return Err(anyhow::anyhow!("Error executing script: {}", e));
}
*speed = get_value_reference(speed_handle)? as u32;
}
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any { self }
fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self }
}
fn parameter_to_rune_value(parameter: &Parameter) -> Result<Value, VmError> {
match parameter {
Parameter::Bool(b) => rune::to_value(*b),
Parameter::Int(i) => rune::to_value(*i),
Parameter::Float(f) => rune::to_value(*f),
Parameter::String(s) => rune::to_value(s.str()),
}
}

View File

@ -0,0 +1,179 @@
use crate::dynamic_data::{ItemScript, Script, ScriptCategory, ScriptOwnerData, ScriptResolver};
use crate::script_implementations::rune::script::RuneScript;
use crate::script_implementations::rune::RuneScriptType;
use crate::static_data::Item;
use crate::StringKey;
use hashbrown::HashMap;
use parking_lot::RwLock;
use rune::compile::meta::AssociatedKind;
use rune::compile::{ComponentRef, MetaError};
use rune::diagnostics::Diagnostic;
use rune::runtime::{RuntimeContext, Shared};
use rune::{Context, Diagnostics, Hash, Options, Source, Sources, Unit, Vm};
use std::any::Any;
use std::path::Path;
use std::sync::Arc;
#[derive(Debug)]
pub struct RuneScriptResolver {
runtime: Arc<RuntimeContext>,
unit: Arc<Unit>,
script_types: HashMap<(ScriptCategory, StringKey), Arc<RuneScriptType>>,
}
pub struct RuneScriptResolverBuilder {
context: Context,
scripts: Sources,
}
impl ScriptResolver for RuneScriptResolver {
fn load_script(
&self,
owner: ScriptOwnerData,
category: ScriptCategory,
script_key: &StringKey,
) -> anyhow::Result<Option<Arc<dyn Script>>> {
let script_type = if let Some(script_type) = self.script_types.get(&(category, script_key.clone())) {
script_type
} else {
return Ok(None);
};
let state = Shared::new(rune::runtime::Object::new())?;
let script = Arc::new(RuneScript::new(
script_key.clone(),
state,
owner,
script_type.clone(),
self.runtime.clone(),
self.unit.clone(),
));
Ok(Some(script))
}
fn load_item_script(&self, _key: &dyn Item) -> anyhow::Result<Option<Arc<dyn ItemScript>>> { Ok(None) }
fn as_any(&self) -> &dyn Any { self }
}
impl RuneScriptResolverBuilder {
pub fn new() -> anyhow::Result<Self> {
let mut context = Context::with_default_modules()?;
context.install(super::wrappers::module()?)?;
Ok(Self {
context,
scripts: Sources::default(),
})
}
pub fn insert_script(&mut self, path: &Path, name: &str, script: &str) -> anyhow::Result<&mut Self> {
self.scripts
.insert(Source::with_path(name, script.to_string(), path)?)?;
Ok(self)
}
pub fn build(mut self) -> anyhow::Result<Arc<dyn ScriptResolver>> {
let mut visitor = FindScriptTypeVisitor::default();
let mut diagnostics = Diagnostics::new();
let mut options = Options::default();
options.debug_info(true);
options.memoize_instance_fn(true);
options.macros(true);
options.bytecode(true);
let result = rune::prepare(&mut self.scripts)
.with_context(&self.context)
.with_visitor(&mut visitor)?
.with_diagnostics(&mut diagnostics)
.with_options(&options)
.build();
if diagnostics.has_error() {
let error_message = diagnostics
.diagnostics()
.iter()
.filter_map(|d| match d {
Diagnostic::Fatal(f) => Some(f.to_string()),
_ => None,
})
.collect::<Vec<String>>()
.join("\n");
return Err(anyhow::anyhow!("Error building Rune script: {}", error_message));
}
let mut script_types = HashMap::with_capacity(visitor.script_types.len());
for (key, script_type) in visitor.script_types {
script_types.insert(key, Arc::new(script_type));
}
Ok(Arc::new(RuneScriptResolver {
runtime: Arc::new(self.context.runtime()?),
unit: Arc::new(result?),
script_types,
}))
}
}
#[derive(Debug, Default)]
struct FindScriptTypeVisitor {
script_types: HashMap<(ScriptCategory, StringKey), RuneScriptType>,
}
impl rune::compile::CompileVisitor for FindScriptTypeVisitor {
fn register_meta(&mut self, meta: rune::compile::MetaRef<'_>) -> Result<(), MetaError> {
match meta.kind {
rune::compile::meta::Kind::Struct { .. } => {
if meta.item.iter().count() < 2 {
return Ok(());
}
let mod_name = meta.item.iter().nth(0).unwrap();
let category = match get_mod_category(mod_name) {
Ok(value) => value,
Err(value) => return value,
};
let name = meta.item.last().unwrap();
self.script_types
.insert((category, name.to_string().as_str().into()), RuneScriptType::default());
}
rune::compile::meta::Kind::Function {
associated: Some(AssociatedKind::Instance(associated)),
..
} => {
if meta.item.iter().count() < 3 {
return Ok(());
}
let mod_name = meta.item.iter().nth(0).unwrap();
let category = match get_mod_category(mod_name) {
Ok(value) => value,
Err(value) => return value,
};
let instance = meta.item.iter().nth_back(1).unwrap();
if let Some(script_type) = self
.script_types
.get_mut(&(category, instance.to_string().as_str().into()))
{
script_type.on_found_fn(associated.to_string().as_str(), meta.hash);
}
}
_ => {}
}
Ok(())
}
}
fn get_mod_category(mod_name: ComponentRef) -> Result<ScriptCategory, Result<(), MetaError>> {
Ok(match mod_name.to_string().as_str() {
"moves" => ScriptCategory::Move,
"abilities" => ScriptCategory::Ability,
"status" => ScriptCategory::Status,
"pokemon" => ScriptCategory::Pokemon,
"sides" => ScriptCategory::Side,
"battle" => ScriptCategory::Battle,
"weather" => ScriptCategory::Weather,
"item_battle_triggers" => ScriptCategory::ItemBattleTrigger,
_ => return Err(Ok(())),
})
}

View File

@ -0,0 +1,26 @@
use crate::dynamic_data::ExecutingMove;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use rune::runtime::{AnyObj, Shared};
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneExecutingMove>()?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneExecutingMove {
inner: Arc<ExecutingMove>,
}
impl RuneExecutingMove {
#[rune::function]
pub fn target_count(&self) -> usize { self.inner.target_count() }
#[rune::function]
pub fn number_of_hits(&self) -> u8 { self.inner.number_of_hits() }
#[rune::function]
pub fn user(&self) -> Shared<AnyObj> { self.inner.user().wrap() }
}
impl_rune_wrapper!(&Arc<ExecutingMove>, RuneExecutingMove);

View File

@ -0,0 +1,67 @@
use rune::runtime::Protocol;
use rune::runtime::{AnyObj, Shared};
use rune::{Any, Value};
use std::ops::Deref;
mod executing_move;
mod pokemon;
mod turn_choice;
pub use executing_move::*;
pub use pokemon::*;
pub trait RuneWrapper {
#[inline]
fn wrap(self) -> Shared<AnyObj>;
}
pub fn module() -> anyhow::Result<rune::Module> {
let mut module = rune::Module::new();
module.ty::<RuneValueIntWrapper>()?;
turn_choice::register(&mut module)?;
pokemon::register(&mut module)?;
executing_move::register(&mut module)?;
Ok(module)
}
pub fn wrap_value_reference(value: i64) -> anyhow::Result<Value> {
Ok(Value::Any(Shared::new(AnyObj::new(RuneValueIntWrapper::new(value))?)?))
}
pub fn get_value_reference(value: Value) -> anyhow::Result<i64> {
let obj = value.into_any().into_result()?;
let obj = obj.take()?;
let obj = obj.downcast_borrow_ref::<RuneValueIntWrapper>().unwrap();
Ok(obj.value())
}
#[derive(Any, Clone)]
struct RuneValueIntWrapper {
#[rune(get, set)]
value: i64,
}
impl RuneValueIntWrapper {
pub fn new(value: i64) -> Self { Self { value } }
pub fn value(&self) -> i64 { self.value }
pub fn set_value(&mut self, value: i64) { self.value = value; }
}
impl RuneWrapper for RuneValueIntWrapper {
fn wrap(self) -> Shared<AnyObj> { Shared::new(AnyObj::new(self).unwrap()).unwrap() }
}
macro_rules! impl_rune_wrapper {
($t:ty, $wrapped_type:ident) => {
impl crate::script_implementations::rune::wrappers::RuneWrapper for $t {
fn wrap(self) -> rune::runtime::Shared<rune::runtime::AnyObj> {
rune::runtime::Shared::new(rune::runtime::AnyObj::new($wrapped_type { inner: self.clone() }).unwrap())
.unwrap()
}
}
};
}
pub(self) use impl_rune_wrapper;

View File

@ -0,0 +1,22 @@
use crate::defines::LevelInt;
use crate::dynamic_data::Pokemon;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use rune::Any;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RunePokemon>()?;
module.function_meta(RunePokemon::level)?;
Ok(())
}
#[derive(Any)]
pub struct RunePokemon {
inner: Pokemon,
}
impl RunePokemon {
#[rune::function]
fn level(&self) -> LevelInt { self.inner.level() }
}
impl_rune_wrapper!(&Pokemon, RunePokemon);

View File

@ -0,0 +1,26 @@
use crate::dynamic_data::TurnChoice;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneExecutingMove, RuneWrapper};
use rune::{Any, Value};
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneTurnChoice>()?;
module.function_meta(RuneTurnChoice::speed)?;
module.function_meta(RuneTurnChoice::user)?;
Ok(())
}
#[derive(Any)]
pub struct RuneTurnChoice {
inner: Arc<TurnChoice>,
}
impl RuneTurnChoice {
#[rune::function]
fn speed(&self) -> u32 { self.inner.speed() }
#[rune::function]
fn user(&self) -> Value { Value::from(self.inner.user().wrap()) }
}
impl_rune_wrapper!(&Arc<TurnChoice>, RuneTurnChoice);

View File

@ -1,5 +1,6 @@
use crate::static_data::Parameter; use crate::static_data::Parameter;
use crate::StringKey; use crate::StringKey;
use hashbrown::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -10,7 +11,7 @@ pub trait SecondaryEffect: Debug {
/// The name of the effect. /// The name of the effect.
fn effect_name(&self) -> &StringKey; fn effect_name(&self) -> &StringKey;
/// A list of parameters for the effect. /// A list of parameters for the effect.
fn parameters(&self) -> &Vec<Arc<Parameter>>; fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>>;
} }
/// A secondary effect is an effect on a move that happens after it hits. /// A secondary effect is an effect on a move that happens after it hits.
@ -21,12 +22,12 @@ pub struct SecondaryEffectImpl {
/// The name of the effect. /// The name of the effect.
effect_name: StringKey, effect_name: StringKey,
/// A list of parameters for the effect. /// A list of parameters for the effect.
parameters: Vec<Arc<Parameter>>, parameters: HashMap<StringKey, Arc<Parameter>>,
} }
impl SecondaryEffectImpl { impl SecondaryEffectImpl {
/// Instantiates a new Secondary Effect. /// Instantiates a new Secondary Effect.
pub fn new(chance: f32, effect_name: StringKey, parameters: Vec<Arc<Parameter>>) -> Self { pub fn new(chance: f32, effect_name: StringKey, parameters: HashMap<StringKey, Arc<Parameter>>) -> Self {
Self { Self {
chance, chance,
effect_name, effect_name,
@ -45,7 +46,7 @@ impl SecondaryEffect for SecondaryEffectImpl {
&self.effect_name &self.effect_name
} }
/// A list of parameters for the effect. /// A list of parameters for the effect.
fn parameters(&self) -> &Vec<Arc<Parameter>> { fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>> {
&self.parameters &self.parameters
} }
} }

View File

@ -1,5 +1,6 @@
use crate::static_data::Parameter; use crate::static_data::Parameter;
use crate::StringKey; use crate::StringKey;
use hashbrown::HashMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
@ -10,7 +11,7 @@ pub trait Ability: Debug {
/// The name of the script effect of the ability. /// The name of the script effect of the ability.
fn effect(&self) -> &StringKey; fn effect(&self) -> &StringKey;
/// The parameters for the script effect of the ability. /// The parameters for the script effect of the ability.
fn parameters(&self) -> &Vec<Arc<Parameter>>; fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>>;
} }
/// An ability is a passive effect in battle that is attached to a Pokemon. /// An ability is a passive effect in battle that is attached to a Pokemon.
@ -21,12 +22,12 @@ pub struct AbilityImpl {
/// The name of the script effect of the ability. /// The name of the script effect of the ability.
effect: StringKey, effect: StringKey,
/// The parameters for the script effect of the ability. /// The parameters for the script effect of the ability.
parameters: Vec<Arc<Parameter>>, parameters: HashMap<StringKey, Arc<Parameter>>,
} }
impl AbilityImpl { impl AbilityImpl {
/// Instantiates a new ability. /// Instantiates a new ability.
pub fn new(name: &StringKey, effect: &StringKey, parameters: Vec<Arc<Parameter>>) -> Self { pub fn new(name: &StringKey, effect: &StringKey, parameters: HashMap<StringKey, Arc<Parameter>>) -> Self {
Self { Self {
name: name.clone(), name: name.clone(),
effect: effect.clone(), effect: effect.clone(),
@ -37,17 +38,11 @@ impl AbilityImpl {
impl Ability for AbilityImpl { impl Ability for AbilityImpl {
/// The name of the ability. /// The name of the ability.
fn name(&self) -> &StringKey { fn name(&self) -> &StringKey { &self.name }
&self.name
}
/// The name of the script effect of the ability. /// The name of the script effect of the ability.
fn effect(&self) -> &StringKey { fn effect(&self) -> &StringKey { &self.effect }
&self.effect
}
/// The parameters for the script effect of the ability. /// The parameters for the script effect of the ability.
fn parameters(&self) -> &Vec<Arc<Parameter>> { fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>> { &self.parameters }
&self.parameters
}
} }
/// 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

View File

@ -5,7 +5,7 @@ use std::fs::File;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::sync::Arc; use std::sync::Arc;
use hashbrown::HashSet; use hashbrown::{HashMap, HashSet};
use num_traits::PrimInt; use num_traits::PrimInt;
use project_root::get_project_root; use project_root::get_project_root;
use serde_json::Value; use serde_json::Value;
@ -223,10 +223,11 @@ pub fn load_abilities(path: &String) -> Arc<dyn AbilityLibrary> {
if let Some(e) = value.get("effect") { if let Some(e) = value.get("effect") {
effect = e.as_str().unwrap().into(); effect = e.as_str().unwrap().into();
} }
let mut parameters = Vec::new(); let mut parameters = HashMap::new();
if let Some(p) = value.get("parameters") { if let Some(pars) = value.get("parameters") {
for par in p.as_array().unwrap() { let pars = pars.as_object().unwrap();
parameters.push(parse_parameter(par)); for par in pars {
parameters.insert(par.0.as_str().into(), parse_parameter(par.1));
} }
} }
@ -258,11 +259,11 @@ pub fn load_moves(path: &String, types: &Arc<dyn TypeLibrary>) -> Arc<dyn MoveLi
if let Some(chance_value) = v.get("chance") { if let Some(chance_value) = v.get("chance") {
chance = chance_value.as_f64().unwrap() as f32; chance = chance_value.as_f64().unwrap() as f32;
} }
let mut parameters = Vec::new(); let mut parameters = HashMap::new();
if let Some(pars) = v.get("parameters") { if let Some(pars) = v.get("parameters") {
let pars = pars.as_array().unwrap(); let pars = pars.as_object().unwrap();
for par in pars { for par in pars {
parameters.push(parse_parameter(par)); parameters.insert(par.0.as_str().into(), parse_parameter(par.1));
} }
} }
@ -451,10 +452,8 @@ fn parse_evolution(value: &Value) -> EvolutionData {
EvolutionData::new(method, species) EvolutionData::new(method, species)
} }
#[cfg(not(feature = "wasm"))] #[cfg(not(any(feature = "wasm", feature = "rune")))]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> { fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> { Arc::new(EmptyScriptResolver::default()) }
Arc::new(EmptyScriptResolver::default())
}
#[cfg(feature = "wasm")] #[cfg(feature = "wasm")]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> { fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
@ -468,6 +467,28 @@ fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
resolver resolver
} }
#[cfg(feature = "rune")]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
let mut builder =
pkmn_lib::script_implementations::rune::script_resolver::RuneScriptResolverBuilder::new().unwrap();
// Recursively load all scripts in the scripts folder
for entry in walkdir::WalkDir::new(path.to_string() + "scripts/") {
let entry = entry.unwrap();
let path = entry.path();
if path.is_file() {
let file = File::open(&path).unwrap();
let mut reader = BufReader::new(file);
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer).unwrap();
builder
.insert_script(path, &path.to_string_lossy(), &String::from_utf8(buffer).unwrap())
.unwrap();
}
}
let resolver = builder.build().unwrap();
resolver
}
fn parse_form( fn parse_form(
name: StringKey, name: StringKey,
value: &Value, value: &Value,

View File

@ -1,285 +1,292 @@
{ {
"adaptability": { "adaptability": {
"effect": "IncreasedStab" "effect": "IncreasedStab"
}, },
"aerilate": { "aerilate": {
"effect": "ChangeMoveType", "effect": "ChangeMoveType",
"parameters": ["normal", "flying"] "parameters": {
}, "from": "normal",
"aftermath": { "to": "flying"
"effect": "Aftermath" }
}, },
"air_lock": { "aftermath": {
"effect": "SuppressWeather" "effect": "Aftermath"
}, },
"analytic": { "air_lock": {
"effect": "Analytic" "effect": "SuppressWeather"
}, },
"anger_point": { "analytic": {
"effect": "AngerPoint" "effect": "Analytic"
}, },
"anticipation": { "anger_point": {
"effect": "Anticipation" "effect": "AngerPoint"
}, },
"arena_trap": { "anticipation": {
"effect": "ArenaTrap" "effect": "Anticipation"
}, },
"aroma_veil": { "arena_trap": {
"effect": "AromaVeil" "effect": "ArenaTrap"
}, },
"aura_break": { "aroma_veil": {
"effect": "AuraBreal" "effect": "AromaVeil"
}, },
"bad_dreams": { "aura_break": {
"effect": "BadDreams" "effect": "AuraBreal"
}, },
"battery": { "bad_dreams": {
"effect": "Battery" "effect": "BadDreams"
}, },
"battle_armor": { "battery": {
"effect": "PreventCritical" "effect": "Battery"
}, },
"battle_bond": { "battle_armor": {
"effect": "BattleBond" "effect": "PreventCritical"
}, },
"beast_boost": { "battle_bond": {
"effect": "BeastBoost" "effect": "BattleBond"
}, },
"berserk": { "beast_boost": {
"effect": "Berserk" "effect": "BeastBoost"
}, },
"big_pecks": { "berserk": {
"effect": "PreventDefLowering" "effect": "Berserk"
}, },
"blaze": { "big_pecks": {
"effect": "PowerUpType", "effect": "PreventDefLowering"
"parameters": ["fire"] },
}, "blaze": {
"bulletproof": { "effect": "PowerUpType",
"effect": "Bulletproof" "parameters": {
}, "type": "fire"
"cheek_pouch": { }
"effect": "CheekPouch" },
}, "bulletproof": {
"chlorophyll": { "effect": "Bulletproof"
"effect": "DoubleSpeedInWeather", },
"parameters": ["HarshSunlight"] "cheek_pouch": {
}, "effect": "CheekPouch"
"clear_body": { },
"effect": "PreventStatLowering" "chlorophyll": {
}, "effect": "DoubleSpeedInWeather",
"cloud_nine": { "parameters": {
"effect": "SuppressWeather" "weather": "HarshSunlight"
}, }
"color_change": { },
"effect": "ColorChange" "clear_body": {
}, "effect": "PreventStatLowering"
"comatose": {}, },
"competitive": {}, "cloud_nine": {
"compound_eyes": {}, "effect": "SuppressWeather"
"contrary": {}, },
"corrosion": {}, "color_change": {
"cursed_body": {}, "effect": "ColorChange"
"cute_charm": {}, },
"damp": {}, "comatose": {},
"dancer": {}, "competitive": {},
"dark_aura": {}, "compound_eyes": {},
"dazzling": {}, "contrary": {},
"defeatist": {}, "corrosion": {},
"defiant": {}, "cursed_body": {},
"delta_stream": {}, "cute_charm": {},
"desolate_land": {}, "damp": {},
"disguise": {}, "dancer": {},
"download": {}, "dark_aura": {},
"drizzle": {}, "dazzling": {},
"drought": {}, "defeatist": {},
"dry_skin": {}, "defiant": {},
"early_bird": {}, "delta_stream": {},
"effect_spore": {}, "desolate_land": {},
"electric_surge": {}, "disguise": {},
"emergency_exit": {}, "download": {},
"fairy_aura": {}, "drizzle": {},
"filter": {}, "drought": {},
"flame_body": {}, "dry_skin": {},
"flare_boost": {}, "early_bird": {},
"flash_fire": {}, "effect_spore": {},
"flower_gift": {}, "electric_surge": {},
"flower_veil": {}, "emergency_exit": {},
"fluffy": {}, "fairy_aura": {},
"forecast": {}, "filter": {},
"forewarn": {}, "flame_body": {},
"friend_guard": {}, "flare_boost": {},
"frisk": {}, "flash_fire": {},
"full_metal_body": {}, "flower_gift": {},
"fur_coat": {}, "flower_veil": {},
"gale_wings": {}, "fluffy": {},
"galvanize": {}, "forecast": {},
"gluttony": {}, "forewarn": {},
"gooey": {}, "friend_guard": {},
"grass_pelt": {}, "frisk": {},
"grassy_surge": {}, "full_metal_body": {},
"guts": {}, "fur_coat": {},
"harvest": {}, "gale_wings": {},
"healer": {}, "galvanize": {},
"heatproof": {}, "gluttony": {},
"heavy_metal": {}, "gooey": {},
"honey_gather": {}, "grass_pelt": {},
"huge_power": {}, "grassy_surge": {},
"hustle": {}, "guts": {},
"hydration": {}, "harvest": {},
"hyper_cutter": {}, "healer": {},
"ice_body": {}, "heatproof": {},
"illuminate": {}, "heavy_metal": {},
"illusion": {}, "honey_gather": {},
"immunity": {}, "huge_power": {},
"imposter": {}, "hustle": {},
"infiltrator": {}, "hydration": {},
"innards_out": {}, "hyper_cutter": {},
"inner_focus": {}, "ice_body": {},
"insomnia": {}, "illuminate": {},
"intimidate": {}, "illusion": {},
"iron_barbs": {}, "immunity": {},
"iron_fist": {}, "imposter": {},
"justified": {}, "infiltrator": {},
"keen_eye": {}, "innards_out": {},
"klutz": {}, "inner_focus": {},
"leaf_guard": {}, "insomnia": {},
"levitate": {}, "intimidate": {},
"light_metal": {}, "iron_barbs": {},
"lightning_rod": {}, "iron_fist": {},
"limber": {}, "justified": {},
"liquid_ooze": {}, "keen_eye": {},
"liquid_voice": {}, "klutz": {},
"long_reach": {}, "leaf_guard": {},
"magic_bounce": {}, "levitate": {},
"magic_guard": {}, "light_metal": {},
"magician": {}, "lightning_rod": {},
"magma_armor": {}, "limber": {},
"magnet_pull": {}, "liquid_ooze": {},
"marvel_scale": {}, "liquid_voice": {},
"mega_launcher": {}, "long_reach": {},
"merciless": {}, "magic_bounce": {},
"minus": {}, "magic_guard": {},
"misty_surge": {}, "magician": {},
"mold_breaker": {}, "magma_armor": {},
"moody": {}, "magnet_pull": {},
"motor_drive": {}, "marvel_scale": {},
"moxie": {}, "mega_launcher": {},
"multiscale": {}, "merciless": {},
"multitype": {}, "minus": {},
"mummy": {}, "misty_surge": {},
"natural_cure": {}, "mold_breaker": {},
"no_guard": {}, "moody": {},
"normalize": {}, "motor_drive": {},
"oblivious": {}, "moxie": {},
"overcoat": {}, "multiscale": {},
"overgrow": {}, "multitype": {},
"own_tempo": {}, "mummy": {},
"parental_bond": {}, "natural_cure": {},
"pickpocket": {}, "no_guard": {},
"pickup": {}, "normalize": {},
"pixilate": {}, "oblivious": {},
"plus": {}, "overcoat": {},
"poison_heal": {}, "overgrow": {},
"poison_point": {}, "own_tempo": {},
"poison_touch": {}, "parental_bond": {},
"power_construct": {}, "pickpocket": {},
"power_of_alchemy": {}, "pickup": {},
"prankster": {}, "pixilate": {},
"pressure": {}, "plus": {},
"primordial_sea": {}, "poison_heal": {},
"prism_armor": {}, "poison_point": {},
"protean": {}, "poison_touch": {},
"psychic_surge": {}, "power_construct": {},
"pure_power": {}, "power_of_alchemy": {},
"queenly_majesty": {}, "prankster": {},
"quick_feet": {}, "pressure": {},
"rain_dish": {}, "primordial_sea": {},
"rattled": {}, "prism_armor": {},
"receiver": {}, "protean": {},
"reckless": {}, "psychic_surge": {},
"refrigerate": {}, "pure_power": {},
"regenerator": {}, "queenly_majesty": {},
"rivalry": {}, "quick_feet": {},
"rks_system": {}, "rain_dish": {},
"rock_head": {}, "rattled": {},
"rough_skin": {}, "receiver": {},
"run_away": {}, "reckless": {},
"sand_force": {}, "refrigerate": {},
"sand_rush": {}, "regenerator": {},
"sand_stream": {}, "rivalry": {},
"sand_veil": {}, "rks_system": {},
"sap_sipper": {}, "rock_head": {},
"schooling": {}, "rough_skin": {},
"scrappy": {}, "run_away": {},
"serene_grace": {}, "sand_force": {},
"shadow_shield": {}, "sand_rush": {},
"shadow_tag": {}, "sand_stream": {},
"shed_skin": {}, "sand_veil": {},
"sheer_force": {}, "sap_sipper": {},
"shell_armor": {}, "schooling": {},
"shield_dust": {}, "scrappy": {},
"shields_down": {}, "serene_grace": {},
"simple": {}, "shadow_shield": {},
"skill_link": {}, "shadow_tag": {},
"slow_start": {}, "shed_skin": {},
"slush_rush": {}, "sheer_force": {},
"sniper": {}, "shell_armor": {},
"snow_cloak": {}, "shield_dust": {},
"snow_warning": {}, "shields_down": {},
"solar_power": {}, "simple": {},
"solid_rock": {}, "skill_link": {},
"soul_heart": {}, "slow_start": {},
"soundproof": {}, "slush_rush": {},
"speed_boost": {}, "sniper": {},
"stakeout": {}, "snow_cloak": {},
"stall": {}, "snow_warning": {},
"stamina": {}, "solar_power": {},
"stance_change": {}, "solid_rock": {},
"static": {}, "soul_heart": {},
"steadfast": {}, "soundproof": {},
"steelworker": {}, "speed_boost": {},
"stench": {}, "stakeout": {},
"sticky_hold": {}, "stall": {},
"storm_drain": {}, "stamina": {},
"strong_jaw": {}, "stance_change": {},
"sturdy": {}, "static": {},
"suction_cups": {}, "steadfast": {},
"super_luck": {}, "steelworker": {},
"surge_surfer": {}, "stench": {},
"swarm": {}, "sticky_hold": {},
"sweet_veil": {}, "storm_drain": {},
"swift_swim": {}, "strong_jaw": {},
"symbiosis": {}, "sturdy": {},
"synchronize": {}, "suction_cups": {},
"tangled_feet": {}, "super_luck": {},
"tangling_hair": {}, "surge_surfer": {},
"technician": {}, "swarm": {},
"telepathy": {}, "sweet_veil": {},
"teravolt": {}, "swift_swim": {},
"thick_fat": {}, "symbiosis": {},
"tinted_lens": {}, "synchronize": {},
"torrent": {}, "tangled_feet": {},
"tough_claws": {}, "tangling_hair": {},
"toxic_boost": {}, "technician": {},
"trace": {}, "telepathy": {},
"triage": {}, "teravolt": {},
"truant": {}, "thick_fat": {},
"turboblaze": {}, "tinted_lens": {},
"unaware": {}, "torrent": {},
"unburden": {}, "tough_claws": {},
"unnerve": {}, "toxic_boost": {},
"victory_star": {}, "trace": {},
"vital_spirit": {}, "triage": {},
"volt_absorb": {}, "truant": {},
"water_absorb": {}, "turboblaze": {},
"water_bubble": {}, "unaware": {},
"water_compaction": {}, "unburden": {},
"water_veil": {}, "unnerve": {},
"weak_armor": {}, "victory_star": {},
"white_smoke": {}, "vital_spirit": {},
"wimp_out": {}, "volt_absorb": {},
"wonder_guard": {}, "water_absorb": {},
"wonder_skin": {}, "water_bubble": {},
"zen_mode": {} "water_compaction": {},
"water_veil": {},
"weak_armor": {},
"white_smoke": {},
"wimp_out": {},
"wonder_guard": {},
"wonder_skin": {},
"zen_mode": {}
} }

View File

@ -29,9 +29,9 @@
"effect": { "effect": {
"name": "drain", "name": "drain",
"chance": -1, "chance": -1,
"parameters": [ "parameters": {
0.5 "drain_mod": 0.5
] }
} }
}, },
{ {
@ -65,9 +65,9 @@
"effect": { "effect": {
"name": "change_target_special_defense", "name": "change_target_special_defense",
"chance": 10, "chance": 10,
"parameters": [ "parameters": {
-1 "amount": -1
] }
} }
}, },
{ {
@ -85,9 +85,9 @@
"effect": { "effect": {
"name": "change_target_defense", "name": "change_target_defense",
"chance": -1, "chance": -1,
"parameters": [ "parameters": {
2 "amount": 2
] }
} }
}, },
{ {
@ -129,9 +129,9 @@
"effect": { "effect": {
"name": "change_target_special_defense", "name": "change_target_special_defense",
"chance": -1, "chance": -1,
"parameters": [ "parameters": {
-2 "amount": -2
] }
} }
}, },
{ {
@ -233,9 +233,9 @@
], ],
"effect": { "effect": {
"name": "change_target_speed", "name": "change_target_speed",
"parameters": [ "parameters": {
2 "amount": 2
] }
} }
}, },
{ {
@ -324,9 +324,9 @@
], ],
"effect": { "effect": {
"name": "change_target_special_defense", "name": "change_target_special_defense",
"parameters": [ "parameters": {
2 "amount": 2
] }
} }
}, },
{ {
@ -363,9 +363,9 @@
"effect": { "effect": {
"name": "change_all_target_stats", "name": "change_all_target_stats",
"chance": 10, "chance": 10,
"parameters": [ "parameters": {
1 "amount": 1
] }
} }
}, },
{ {
@ -397,9 +397,9 @@
], ],
"effect": { "effect": {
"name": "heal_each_end_of_turn", "name": "heal_each_end_of_turn",
"parameters": [ "parameters": {
6.25 "percent": 6.25
] }
} }
}, },
{ {
@ -466,9 +466,9 @@
], ],
"effect": { "effect": {
"name": "change_target_special_defense", "name": "change_target_special_defense",
"parameters": [ "parameters": {
1 "amount": 1
] }
} }
}, },
{ {
@ -592,9 +592,9 @@
"effect": { "effect": {
"name": "change_target_attack", "name": "change_target_attack",
"chance": 10, "chance": 10,
"parameters": [ "parameters": {
-1 "amount": -1
] }
} }
}, },
{ {
@ -660,9 +660,9 @@
], ],
"effect": { "effect": {
"name": "change_target_attack", "name": "change_target_attack",
"parameters": [ "parameters": {
-1 "amount": -1
] }
} }
}, },
{ {
@ -3805,11 +3805,11 @@
"ignore-substitute" "ignore-substitute"
], ],
"effect": { "effect": {
"name": "ChangeTargetAtt", "name": "change_target_attack",
"chance": -1, "chance": -1,
"parameters": [ "parameters": {
-1 "amount": -1
] }
} }
}, },
{ {
@ -5082,11 +5082,11 @@
"mirror" "mirror"
], ],
"effect": { "effect": {
"name": "ChangeTargetDef", "name": "change_target_defense",
"chance": -1, "chance": -1,
"parameters": [ "parameters": {
-1 "amount": -1
] }
} }
}, },
{ {

View File

@ -0,0 +1,13 @@
mod moves {
struct TestMove;
impl TestMove {
pub fn change_speed(self, choice, speed) {
println(`change_speed: ${choice.speed()}`);
println(`user level: ${choice.user().level()}`);
speed.value = 100;
}
}
}

View File

@ -6,8 +6,8 @@
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use pkmn_lib::dynamic_data::{ use pkmn_lib::dynamic_data::{
Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PokemonBuilder, PokemonParty, Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PassChoice, PokemonBuilder,
ScriptCategory, ScriptContainer, ScriptOwnerData, TurnChoice, VolatileScriptsOwner, PokemonParty, ScriptCategory, ScriptContainer, ScriptOwnerData, TurnChoice, VolatileScriptsOwner,
}; };
use crate::common::library_loader; use crate::common::library_loader;
@ -17,9 +17,7 @@ pub mod datatests;
static LIBRARY: LazyLock<Arc<dyn DynamicLibrary>> = LazyLock::new(|| library_loader::load_library().library); static LIBRARY: LazyLock<Arc<dyn DynamicLibrary>> = LazyLock::new(|| library_loader::load_library().library);
fn get_library() -> Arc<dyn DynamicLibrary> { fn get_library() -> Arc<dyn DynamicLibrary> { LIBRARY.clone() }
LIBRARY.clone()
}
#[test] #[test]
fn validate_library_load() { fn validate_library_load() {
@ -35,7 +33,7 @@ fn validate_library_load() {
\n\t- Abilities load time: {} ms\ \n\t- Abilities load time: {} ms\
\n\t- Moves load time: {} ms\ \n\t- Moves load time: {} ms\
\n\t- Species load time: {} ms\ \n\t- Species load time: {} ms\
\n\t- WASM load time: {} ms\ \n\t- Script load time: {} ms\
", ",
(end_time - start_time).num_milliseconds(), (end_time - start_time).num_milliseconds(),
result.types_load_time.num_milliseconds(), result.types_load_time.num_milliseconds(),
@ -49,6 +47,24 @@ fn validate_library_load() {
); );
} }
#[test]
fn rune_test() {
let result = library_loader::load_library();
let library = result.library;
let script = library
.load_script(ScriptOwnerData::None, ScriptCategory::Move, &"TestMove".into())
.unwrap()
.unwrap();
assert_eq!(script.name().unwrap().str(), "TestMove");
let p1 = PokemonBuilder::new(library.clone(), "charizard".into(), 100)
.build()
.unwrap();
let turn_choice = Arc::new(TurnChoice::Pass(PassChoice::new(p1)));
let mut speed = 0;
script.change_speed(&turn_choice, &mut speed).unwrap();
assert_eq!(speed, 100);
}
#[test] #[test]
fn load_non_existing_wasm_script() { fn load_non_existing_wasm_script() {
let start_time = chrono::Utc::now(); let start_time = chrono::Utc::now();
@ -63,7 +79,7 @@ fn load_non_existing_wasm_script() {
\n\t- Abilities load time: {} ms\ \n\t- Abilities load time: {} ms\
\n\t- Moves load time: {} ms\ \n\t- Moves load time: {} ms\
\n\t- Species load time: {} ms\ \n\t- Species load time: {} ms\
\n\t- WASM load time: {} ms\ \n\t- Script load time: {} ms\
", ",
(end_time - start_time).num_milliseconds(), (end_time - start_time).num_milliseconds(),
result.types_load_time.num_milliseconds(), result.types_load_time.num_milliseconds(),