Compare commits

...

6 Commits

Author SHA1 Message Date
Deukhoofd 87c2ddd7b7
Update to rune 0.14, more tests
continuous-integration/drone/push Build is failing Details
2024-05-19 16:37:19 +02:00
Deukhoofd 535f6bf79b
Unit tests for basic Rune registration
continuous-integration/drone/push Build is failing Details
2024-05-18 19:24:47 +02:00
Deukhoofd 42bee5e37c
Large amounts of work on Rune
continuous-integration/drone/push Build is failing Details
2024-05-11 16:01:04 +02:00
Deukhoofd 4ec07ca049
Large amounts of work on Rune
continuous-integration/drone/push Build is failing Details
2024-05-08 15:46:09 +02:00
Deukhoofd 4bc76b0ee4
More work on Rune
continuous-integration/drone/push Build is failing Details
2024-04-13 10:47:40 +02:00
Deukhoofd 67b0abe59f
Initial work on rune as scripting library
continuous-integration/drone/push Build is failing Details
2024-04-07 18:55:46 +02:00
75 changed files with 3925 additions and 1459 deletions

View File

@ -16,7 +16,8 @@ path = "src/lib.rs"
ffi = []
serde = ["dep:serde", "dep:serde-xml-rs", "atomig/serde"]
wasm = ["dep:wasmer"]
default = ["serde", "wasm", "ffi"]
rune = ["dep:rune"]
default = ["serde", "rune", "ffi"]
[profile.dev]
opt-level = 0
@ -50,27 +51,30 @@ chrono = "0.4"
rand = "0.8"
rand_pcg = "0.3"
hashbrown = "0.14"
indexmap = "2.0"
indexmap = "2.2"
parking_lot = "0.12"
serde = { version = "1.0", optional = true, features = ["derive"] }
serde_repr = "0.1"
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.8"
paste = { version = "1.0" }
arcstr = { version = "1.1", features = ["std"] }
arcstr = { version = "1.2", features = ["std"] }
enum-display-derive = "0.1"
anyhow = "1.0"
anyhow_ext = "0.2"
thiserror = "1.0"
stdext = "0.3"
wasmer = { version = "4.2", optional = true, default-features = false, features = ["sys", "wat", "llvm"] }
rune = { version = "0.14.0", optional = true, git = "https://github.com/Deukhoofd/rune", branch = "equality_symmetry" }
[dev-dependencies]
csv = "1.3"
project-root = "0.2"
serde_yaml = "0.9"
serde_yml = "0.0.7"
serde_json = "1.0"
serde_plain = "1.0"
# Allow us to assert whether floats are approximately a value
assert_approx_eq = "1.1"
mockall = "0.11"
mockall = "0.12"
walkdir = "2.3"

View File

@ -1 +1,4 @@
max_width = 120
max_width = 120
fn_single_line = true
inline_attribute_width = 120
unstable_features = true

View File

@ -56,47 +56,33 @@ impl TurnChoice {
}
/// Get the user of the given choice.
pub fn user(&self) -> &Pokemon {
&self.choice_data().user
}
pub fn user(&self) -> &Pokemon { &self.choice_data().user }
/// Get the speed of the user for the choice. Note that this speed is the speed of the Pokemon
/// at the start of the turn!
pub fn speed(&self) -> u32 {
self.choice_data().speed.load(Ordering::Relaxed)
}
pub fn speed(&self) -> u32 { self.choice_data().speed.load(Ordering::Relaxed) }
/// Sets the speed of user for the choice. Note that this speed is the speed of the Pokemon at
/// the start of the turn!
pub fn set_speed(&self, value: u32) {
self.choice_data().speed.store(value, Ordering::Relaxed);
}
pub fn set_speed(&self, value: u32) { self.choice_data().speed.store(value, Ordering::Relaxed); }
/// Gets whether or not the choice has failed. If we notice this when we execute the choice, we
/// will not execute it.
pub fn has_failed(&self) -> bool {
self.choice_data().has_failed.load(Ordering::SeqCst)
}
pub fn has_failed(&self) -> bool { self.choice_data().has_failed.load(Ordering::SeqCst) }
/// Fails the choice. This will prevent it from executing and run a specific fail handling during
/// execution. Note that this can not be undone.
pub fn fail(&self) {
self.choice_data().has_failed.store(true, Ordering::SeqCst)
}
pub fn fail(&self) { self.choice_data().has_failed.store(true, Ordering::SeqCst) }
/// The random value of a turn choice gets set during the start of a choice, and is used for tie
/// breaking of turn executions. This means that choices get executed with a predictable order,
/// regardless of implementation details.
pub(crate) fn random_value(&self) -> u32 {
self.choice_data().random_value.load(Ordering::Relaxed)
}
pub(crate) fn random_value(&self) -> u32 { self.choice_data().random_value.load(Ordering::Relaxed) }
/// This sets the above random value.
pub(crate) fn set_random_value(&self, val: u32) {
self.choice_data().random_value.store(val, Ordering::Relaxed)
}
pub(crate) fn set_random_value(&self, val: u32) { self.choice_data().random_value.store(val, Ordering::Relaxed) }
/// Helper function to get the move choice data from a turn. Note that this will panic if not
/// Helper function to get the move choice data from a turn. Note that this will error if not
/// used on a move choice.
pub(crate) fn get_move_turn_data(&self) -> Result<&MoveChoice> {
if let TurnChoice::Move(data) = self {
@ -187,48 +173,28 @@ impl MoveChoice {
}
/// The actual learned move on the Pokemon we use for this choice.
pub fn used_move(&self) -> &Arc<LearnedMove> {
&self.used_move
}
pub fn used_move(&self) -> &Arc<LearnedMove> { &self.used_move }
/// The target side the move is aimed at.
pub fn target_side(&self) -> u8 {
self.target_side
}
pub fn target_side(&self) -> u8 { self.target_side }
/// The Pokemon index on the side we're aiming at.
pub fn target_index(&self) -> u8 {
self.target_index
}
pub fn target_index(&self) -> u8 { self.target_index }
/// The priority of the move choice at the beginning of the turn.
pub fn priority(&self) -> i8 {
self.priority.load(Ordering::Relaxed)
}
pub fn priority(&self) -> i8 { self.priority.load(Ordering::Relaxed) }
/// The priority of the move choice at the beginning of the turn.
pub fn set_priority(&self, value: i8) {
self.priority.store(value, Ordering::Relaxed)
}
pub fn set_priority(&self, value: i8) { self.priority.store(value, Ordering::Relaxed) }
/// The user of the choice.
pub fn user(&self) -> &Pokemon {
&self.choice_data.user
}
pub fn user(&self) -> &Pokemon { &self.choice_data.user }
/// The move script of the choice.
pub fn script(&self) -> &ScriptContainer {
&self.script
}
pub fn script(&self) -> &ScriptContainer { &self.script }
}
impl ScriptSource for MoveChoice {
fn get_script_count(&self) -> Result<usize> {
Ok(self.choice_data.user.get_script_count()? + 1)
}
fn get_script_count(&self) -> Result<usize> { Ok(self.choice_data.user.get_script_count()? + 1) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.choice_data.script_source_data }
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.script).into());
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { scripts.push((&self.script).into()); }
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts);
@ -260,13 +226,9 @@ impl ItemChoice {
}
impl ScriptSource for ItemChoice {
fn get_script_count(&self) -> Result<usize> {
Ok(0)
}
fn get_script_count(&self) -> Result<usize> { Ok(0) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.choice_data.script_source_data }
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
@ -299,13 +261,9 @@ impl SwitchChoice {
}
impl ScriptSource for SwitchChoice {
fn get_script_count(&self) -> Result<usize> {
Ok(0)
}
fn get_script_count(&self) -> Result<usize> { Ok(0) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.choice_data.script_source_data }
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
@ -337,13 +295,9 @@ impl FleeChoice {
}
impl ScriptSource for FleeChoice {
fn get_script_count(&self) -> Result<usize> {
Ok(0)
}
fn get_script_count(&self) -> Result<usize> { Ok(0) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.choice_data.script_source_data }
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
@ -376,13 +330,9 @@ impl PassChoice {
}
impl ScriptSource for PassChoice {
fn get_script_count(&self) -> Result<usize> {
Ok(0)
}
fn get_script_count(&self) -> Result<usize> { Ok(0) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.choice_data.script_source_data }
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
@ -392,17 +342,13 @@ impl ScriptSource for PassChoice {
}
impl PartialEq<Self> for TurnChoice {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
fn eq(&self, other: &Self) -> bool { std::ptr::eq(self, other) }
}
impl Eq for TurnChoice {}
impl PartialOrd for TurnChoice {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
}
impl Ord for TurnChoice {

View File

@ -68,9 +68,7 @@ impl ChoiceQueue {
}
/// Check if we have any choices remaining.
pub fn has_next(&self) -> bool {
self.current.load(Ordering::Relaxed) < self.queue.read().len()
}
pub fn has_next(&self) -> bool { self.current.load(Ordering::Relaxed) < self.queue.read().len() }
/// This resorts the yet to be executed choices. This can be useful for dealing with situations
/// such as Pokemon changing forms just after the very start of a turn, when turn order has
@ -163,7 +161,6 @@ mod tests {
use crate::defines::LevelInt;
use crate::dynamic_data::{DynamicLibrary, PassChoice};
use crate::static_data::{AbilityIndex, Gender};
use std::sync::Arc;
#[test]
fn create_empty_queue() {
let queue = ChoiceQueue::new(Vec::new());

View File

@ -1,9 +1,5 @@
#[doc(inline)]
pub use choice_queue::*;
#[doc(inline)]
pub use target_resolver::*;
#[doc(inline)]
pub use turn_runner::*;
#[doc(inline)] pub use choice_queue::*;
#[doc(inline)] pub use target_resolver::*;
/// Data for enqueueing and retrieving choices.
mod choice_queue;

View File

@ -4,6 +4,7 @@ use crate::static_data::{EvolutionMethod, TimeOfDay};
/// A library for handling the checking of evolution requirements.
pub trait EvolutionLibrary {
/// Checks if the given Pokemon fulfills the given evolution conditions.
#[allow(dead_code)]
fn pokemon_fulfills_evolution_conditions(&self, pokemon: &Pokemon, method: &EvolutionMethod) -> bool;
}

View File

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

View File

@ -125,57 +125,31 @@ impl Battle {
}
/// The library the battle uses for handling.
pub fn library(&self) -> &Arc<dyn DynamicLibrary> {
&self.data.library
}
pub fn library(&self) -> &Arc<dyn DynamicLibrary> { &self.data.library }
/// A list of all different parties in the battle.
pub fn parties(&self) -> &Vec<Arc<BattleParty>> {
&self.data.parties
}
pub fn parties(&self) -> &Vec<Arc<BattleParty>> { &self.data.parties }
/// Whether or not Pokemon can flee from the battle.
pub fn can_flee(&self) -> bool {
self.data.can_flee
}
pub fn can_flee(&self) -> bool { self.data.can_flee }
/// The number of sides in the battle. Typically 2.
pub fn number_of_sides(&self) -> u8 {
self.data.number_of_sides
}
pub fn number_of_sides(&self) -> u8 { self.data.number_of_sides }
/// The number of Pokemon that can be on each side.
pub fn pokemon_per_side(&self) -> u8 {
self.data.pokemon_per_side
}
pub fn pokemon_per_side(&self) -> u8 { self.data.pokemon_per_side }
/// A list of all sides in the battle.
pub fn sides(&self) -> &Vec<BattleSide> {
&self.data.sides
}
pub fn sides(&self) -> &Vec<BattleSide> { &self.data.sides }
/// The RNG used for the battle.
pub fn random(&self) -> &Arc<BattleRandom> {
&self.data.random
}
pub fn random(&self) -> &Arc<BattleRandom> { &self.data.random }
/// Whether or not the battle has ended.
pub fn has_ended(&self) -> bool {
self.data.has_ended.load(Ordering::Relaxed)
}
pub fn has_ended(&self) -> bool { self.data.has_ended.load(Ordering::Relaxed) }
/// The eventual result of the battle. Inconclusive until the battle is ended.
pub fn result(&self) -> BattleResult {
*self.data.result.read()
}
pub fn result(&self) -> BattleResult { *self.data.result.read() }
/// The handler to send all events to.
pub fn event_hook(&self) -> &EventHook {
&self.data.event_hook
}
pub fn event_hook(&self) -> &EventHook { &self.data.event_hook }
/// The index of the current turn. 0 until all choices
pub fn current_turn(&self) -> u32 {
self.data.current_turn.load(Ordering::Relaxed)
}
pub fn current_turn(&self) -> u32 { self.data.current_turn.load(Ordering::Relaxed) }
/// The time in nanoseconds the last turn took to run. Defaults to 0.
pub fn last_turn_time(&self) -> u64 {
self.data.last_turn_time.load(Ordering::Relaxed)
}
pub fn last_turn_time(&self) -> u64 { self.data.last_turn_time.load(Ordering::Relaxed) }
/// A queue of the yet to be executed choices in a turn.
pub fn current_turn_queue(&self) -> &RwLock<Option<Arc<ChoiceQueue>>> {
&self.data.current_turn_queue
}
pub fn current_turn_queue(&self) -> &RwLock<Option<Arc<ChoiceQueue>>> { &self.data.current_turn_queue }
/// Get a Pokemon on the battlefield, on a specific side and an index on that side.
pub fn get_pokemon(&self, side: u8, index: u8) -> Option<Pokemon> {
@ -379,28 +353,21 @@ impl Battle {
}
/// Gets the inner pointer to the reference counted data.
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void }
}
impl PartialEq for Battle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.data, &other.data) }
}
impl WeakBattleReference {
/// Attempts to upgrade the weak reference to a strong reference. If the strong reference has
/// been dropped, this returns None.
pub fn upgrade(&self) -> Option<Battle> {
self.data.upgrade().map(|battle| Battle { data: battle })
}
pub fn upgrade(&self) -> Option<Battle> { self.data.upgrade().map(|battle| Battle { data: battle }) }
/// Gets the inner pointer to the reference counted data.
pub(crate) fn as_ptr(&self) -> *const c_void {
self.data.as_ptr() as *const c_void
}
#[cfg(feature = "wasm")]
pub(crate) fn as_ptr(&self) -> *const c_void { self.data.as_ptr() as *const c_void }
}
unsafe impl Send for WeakBattleReference {}
@ -408,17 +375,13 @@ unsafe impl Send for WeakBattleReference {}
unsafe impl Sync for WeakBattleReference {}
impl PartialEq for WeakBattleReference {
fn eq(&self, other: &Self) -> bool {
self.data.ptr_eq(&other.data)
}
fn eq(&self, other: &Self) -> bool { self.data.ptr_eq(&other.data) }
}
impl Eq for WeakBattleReference {}
impl VolatileScriptsOwner for Battle {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.data.volatile_scripts
}
fn volatile_scripts(&self) -> &Arc<ScriptSet> { &self.data.volatile_scripts }
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
self.data.library.load_script(self.into(), ScriptCategory::Battle, key)
@ -426,13 +389,9 @@ impl VolatileScriptsOwner for Battle {
}
impl ScriptSource for Battle {
fn get_script_count(&self) -> Result<usize> {
Ok(1)
}
fn get_script_count(&self) -> Result<usize> { Ok(1) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.data.script_source_data }
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.data.weather).into());
@ -457,7 +416,5 @@ pub enum BattleResult {
impl BattleResult {
/// Whether or not the battle has a winner.
pub fn is_conclusive(&self) -> bool {
matches!(self, Self::Conclusive(_))
}
pub fn is_conclusive(&self) -> bool { matches!(self, Self::Conclusive(_)) }
}

View File

@ -101,30 +101,18 @@ impl BattleSide {
}
/// The index of the side on the battle.
pub fn index(&self) -> u8 {
self.data.index
}
pub fn index(&self) -> u8 { self.data.index }
/// The number of Pokemon that can be on the side.
pub fn pokemon_per_side(&self) -> u8 {
self.data.pokemon_per_side
}
pub fn pokemon_per_side(&self) -> u8 { self.data.pokemon_per_side }
/// A list of pokemon currently on the battlefield.
pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<Option<Pokemon>>> {
self.data.pokemon.read()
}
pub fn pokemon(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<Option<Pokemon>>> { self.data.pokemon.read() }
/// The currently set choices for all Pokemon on the battlefield. Cleared when the turn starts.
pub fn choices(&self) -> &RwLock<Vec<Option<Arc<TurnChoice>>>> {
&self.data.choices
}
pub fn choices(&self) -> &RwLock<Vec<Option<Arc<TurnChoice>>>> { &self.data.choices }
/// The slots on the side that can still be filled. Once all slots are set to false, this side
/// has lost the battle.
pub fn fillable_slots(&self) -> &Vec<AtomicBool> {
&self.data.fillable_slots
}
pub fn fillable_slots(&self) -> &Vec<AtomicBool> { &self.data.fillable_slots }
/// The number of choices that are set.
pub fn choices_set(&self) -> u8 {
self.data.choices_set.load(Ordering::SeqCst)
}
pub fn choices_set(&self) -> u8 { self.data.choices_set.load(Ordering::SeqCst) }
/// A reference to the battle we're part of.
pub fn battle(&self) -> Result<Battle> {
self.data
@ -133,18 +121,12 @@ impl BattleSide {
.ok_or(anyhow!("Battle was not set, but requested"))
}
/// Whether or not this side has fled.
pub fn has_fled_battle(&self) -> bool {
self.data.has_fled_battle.load(Ordering::SeqCst)
}
pub fn has_fled_battle(&self) -> bool { self.data.has_fled_battle.load(Ordering::SeqCst) }
/// The volatile scripts that are attached to the side.
pub fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.data.volatile_scripts
}
pub fn volatile_scripts(&self) -> &Arc<ScriptSet> { &self.data.volatile_scripts }
/// Whether every Pokemon on this side has its choices
pub fn all_choices_set(&self) -> bool {
self.choices_set() == self.data.pokemon_per_side
}
pub fn all_choices_set(&self) -> bool { self.choices_set() == self.data.pokemon_per_side }
/// Returns true if there are slots that need to be filled with a new pokemon, that have parties
/// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are
@ -190,9 +172,7 @@ impl BattleSide {
}
/// Forcibly removes a Pokemon from the field.
pub fn force_clear_pokemon(&mut self, index: u8) {
self.data.pokemon.write().get_mut(index as usize).take();
}
pub fn force_clear_pokemon(&mut self, index: u8) { self.data.pokemon.write().get_mut(index as usize).take(); }
/// Switches out a spot on the field for a different Pokemon.
pub fn set_pokemon(&self, index: u8, pokemon: Option<Pokemon>) -> Result<()> {
@ -296,9 +276,7 @@ impl BattleSide {
}
/// Mark the side as fled.
pub fn mark_as_fled(&mut self) {
self.data.has_fled_battle.store(true, Ordering::SeqCst);
}
pub fn mark_as_fled(&mut self) { self.data.has_fled_battle.store(true, Ordering::SeqCst); }
/// Gets a random Pokemon on the given side.
pub fn get_random_creature_index(&self) -> Result<u8> {
@ -364,28 +342,21 @@ impl BattleSide {
}
/// Gets the inner pointer to the reference counted data.
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void }
}
impl PartialEq for BattleSide {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.data, &other.data) }
}
impl WeakBattleSideReference {
/// Upgrades the weak reference to a strong reference, returning `None` if the side has been
/// dropped.
pub fn upgrade(&self) -> Option<BattleSide> {
self.data.upgrade().map(|data| BattleSide { data })
}
pub fn upgrade(&self) -> Option<BattleSide> { self.data.upgrade().map(|data| BattleSide { data }) }
/// Gets the underlying pointer to the data of the side.
pub(crate) fn as_ptr(&self) -> *const c_void {
self.data.as_ptr() as *const c_void
}
#[cfg(feature = "wasm")]
pub(crate) fn as_ptr(&self) -> *const c_void { self.data.as_ptr() as *const c_void }
}
unsafe impl Send for WeakBattleSideReference {}
@ -393,15 +364,11 @@ unsafe impl Send for WeakBattleSideReference {}
unsafe impl Sync for WeakBattleSideReference {}
impl PartialEq for WeakBattleSideReference {
fn eq(&self, other: &Self) -> bool {
self.data.ptr_eq(&other.data)
}
fn eq(&self, other: &Self) -> bool { self.data.ptr_eq(&other.data) }
}
impl VolatileScriptsOwner for BattleSide {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.data.volatile_scripts
}
fn volatile_scripts(&self) -> &Arc<ScriptSet> { &self.data.volatile_scripts }
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
self.battle()?
@ -411,17 +378,11 @@ impl VolatileScriptsOwner for BattleSide {
}
impl ScriptSource for BattleSide {
fn get_script_count(&self) -> Result<usize> {
Ok(self.battle()?.get_script_count()? + 1)
}
fn get_script_count(&self) -> Result<usize> { Ok(self.battle()?.get_script_count()? + 1) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.data.script_source_data }
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.data.volatile_scripts).into());
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { scripts.push((&self.data.volatile_scripts).into()); }
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts);

View File

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

View File

@ -23,6 +23,7 @@ pub struct LearnedMove {
#[derive(Copy, Clone, Debug, Default)]
#[repr(u8)]
#[cfg_attr(feature = "serde", derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
pub enum MoveLearnMethod {
/// We do not know the learn method.
#[default]
@ -44,28 +45,18 @@ impl LearnedMove {
}
/// The immutable move information of the move.
pub fn move_data(&self) -> &Arc<dyn MoveData> {
&self.move_data
}
pub fn move_data(&self) -> &Arc<dyn MoveData> { &self.move_data }
/// The maximal power points for this move.
pub fn max_pp(&self) -> u8 {
self.move_data.base_usages() + self.max_pp_modification
}
pub fn max_pp(&self) -> u8 { self.move_data.base_usages() + self.max_pp_modification }
/// The amount by which the maximal power points have been modified for this move.
/// This could for example be due to PP Ups.
pub fn max_pp_modification(&self) -> u8 {
self.max_pp_modification
}
pub fn max_pp_modification(&self) -> u8 { self.max_pp_modification }
/// The amount of remaining power points. If this is 0, we can not use the move anymore.
pub fn remaining_pp(&self) -> u8 {
self.remaining_pp.load(Ordering::Relaxed)
}
pub fn remaining_pp(&self) -> u8 { self.remaining_pp.load(Ordering::Relaxed) }
/// The way the move was learned.
pub fn learn_method(&self) -> MoveLearnMethod {
self.learn_method
}
pub fn learn_method(&self) -> MoveLearnMethod { self.learn_method }
/// Try and reduce the PP by a certain amount. If the amount is higher than the current uses,
/// return false. Otherwise, reduce the PP, and return true.
@ -81,9 +72,7 @@ impl LearnedMove {
}
/// Set the remaining PP to the max amount of PP.
pub fn restore_all_uses(&self) {
self.remaining_pp.store(self.max_pp(), Ordering::SeqCst);
}
pub fn restore_all_uses(&self) { self.remaining_pp.store(self.max_pp(), Ordering::SeqCst); }
/// Restore the remaining PP by a certain amount. Will prevent it from going above max PP.
pub fn restore_uses(&self, mut uses: u8) {

View File

@ -216,21 +216,13 @@ impl Pokemon {
}
/// The library data of the Pokemon.
pub fn library(&self) -> &Arc<dyn DynamicLibrary> {
&self.data.library
}
pub fn library(&self) -> &Arc<dyn DynamicLibrary> { &self.data.library }
/// The species of the Pokemon.
pub fn species(&self) -> Arc<dyn Species> {
self.data.species.read().clone()
}
pub fn species(&self) -> Arc<dyn Species> { self.data.species.read().clone() }
/// The form of the Pokemon.
pub fn form(&self) -> Arc<dyn Form> {
self.data.form.read().clone()
}
pub fn form(&self) -> Arc<dyn Form> { self.data.form.read().clone() }
/// Whether or not the Pokemon is showing as a different species than it actually is.
pub fn has_different_display_species(&self) -> bool {
self.data.display_species.is_some()
}
pub fn has_different_display_species(&self) -> bool { self.data.display_species.is_some() }
/// The species that should be displayed to the user. This handles stuff like the Illusion ability.
pub fn display_species(&self) -> Arc<dyn 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.
pub fn has_different_display_form(&self) -> bool {
self.data.display_form.is_some()
}
pub fn has_different_display_form(&self) -> bool { self.data.display_form.is_some() }
/// The form that should be displayed to the user. This handles stuff like the Illusion ability.
pub fn display_form(&self) -> Arc<dyn Form> {
if let Some(v) = &self.data.display_form {
@ -253,32 +243,20 @@ impl Pokemon {
}
/// The current level of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Level)
pub fn level(&self) -> LevelInt {
self.data.level.load(Ordering::Relaxed)
}
pub fn level(&self) -> LevelInt { self.data.level.load(Ordering::Relaxed) }
/// The amount of experience of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Experience)
pub fn experience(&self) -> u32 {
self.data.experience.load(Ordering::Relaxed)
}
pub fn experience(&self) -> u32 { self.data.experience.load(Ordering::Relaxed) }
/// The personality value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Personality_value)
pub fn personality_value(&self) -> u32 {
self.data.personality_value
}
pub fn personality_value(&self) -> u32 { self.data.personality_value }
/// The gender of the Pokemon.
pub fn gender(&self) -> Gender {
*self.data.gender.read()
}
pub fn gender(&self) -> Gender { *self.data.gender.read() }
/// 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.
pub fn coloring(&self) -> u8 {
self.data.coloring
}
pub fn coloring(&self) -> u8 { self.data.coloring }
/// Gets the held item of a Pokemon
pub fn held_item(&self) -> &RwLock<Option<Arc<dyn Item>>> {
&self.data.held_item
}
pub fn held_item(&self) -> Option<Arc<dyn Item>> { self.data.held_item.read().clone().map(|v| v) }
/// Checks whether the Pokemon is holding a specific item.
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.
@ -292,9 +270,7 @@ impl Pokemon {
self.data.held_item.write().replace(item.clone())
}
/// Removes the held item from the Pokemon. Returns the previously held item.
pub fn remove_held_item(&self) -> Option<Arc<dyn Item>> {
self.data.held_item.write().take()
}
pub fn remove_held_item(&self) -> Option<Arc<dyn Item>> { self.data.held_item.write().take() }
/// Makes the Pokemon uses its held item.
pub fn consume_held_item(&self) -> Result<bool> {
if self.data.held_item.read().is_none() {
@ -316,72 +292,42 @@ impl Pokemon {
}
/// The remaining health points of the Pokemon.
pub fn current_health(&self) -> u32 {
self.data.current_health.load(Ordering::Relaxed)
}
pub fn current_health(&self) -> u32 { self.data.current_health.load(Ordering::Relaxed) }
/// The max health points of the Pokemon.
pub fn max_health(&self) -> u32 {
self.data.boosted_stats.hp()
}
pub fn max_health(&self) -> u32 { self.data.boosted_stats.hp() }
/// The weight of the Pokemon in kilograms.
pub fn weight(&self) -> f32 {
self.data.weight.load(Ordering::Relaxed)
}
pub fn weight(&self) -> f32 { self.data.weight.load(Ordering::Relaxed) }
/// Sets the weight of the Pokemon in kilograms.
pub fn set_weight(&self, weight: f32) {
self.data.weight.store(weight, Ordering::Relaxed)
}
pub fn set_weight(&self, weight: f32) { self.data.weight.store(weight, Ordering::Relaxed) }
/// The height of the Pokemon in meters.
pub fn height(&self) -> f32 {
self.data.height.load(Ordering::Relaxed)
}
pub fn height(&self) -> f32 { self.data.height.load(Ordering::Relaxed) }
/// The current happiness of the Pokemon. Also known as friendship.
pub fn happiness(&self) -> u8 {
self.data.happiness.load(Ordering::Relaxed)
}
pub fn happiness(&self) -> u8 { self.data.happiness.load(Ordering::Relaxed) }
/// An optional nickname of the Pokemon.
pub fn nickname(&self) -> &Option<String> {
&self.data.nickname
}
pub fn nickname(&self) -> &Option<String> { &self.data.nickname }
/// An index of the ability to find the actual ability on the form.
pub fn real_ability(&self) -> &AbilityIndex {
&self.data.ability_index
}
pub fn real_ability(&self) -> &AbilityIndex { &self.data.ability_index }
/// The current types of the Pokemon.
pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<TypeIdentifier>> {
self.data.types.read()
}
pub fn types(&self) -> RwLockReadGuard<'_, RawRwLock, Vec<TypeIdentifier>> { self.data.types.read() }
/// The moves the Pokemon has learned. This is of a set length of [`MAX_MOVES`]. Empty move slots
/// are defined by None.
pub fn learned_moves(&self) -> &RwLock<[Option<Arc<LearnedMove>>; MAX_MOVES]> {
&self.data.moves
}
pub fn learned_moves(&self) -> &RwLock<[Option<Arc<LearnedMove>>; MAX_MOVES]> { &self.data.moves }
/// The stats of the Pokemon when disregarding any stat boosts.
pub fn flat_stats(&self) -> &Arc<StatisticSet<u32>> {
&self.data.flat_stats
}
pub fn flat_stats(&self) -> &Arc<StatisticSet<u32>> { &self.data.flat_stats }
/// The amount of boosts on a specific stat.
pub fn stat_boosts(&self) -> &Arc<ClampedStatisticSet<i8, -6, 6>> {
&self.data.stat_boost
}
pub fn stat_boosts(&self) -> &Arc<ClampedStatisticSet<i8, -6, 6>> { &self.data.stat_boost }
/// Whether or not this Pokemon is still an egg, and therefore cannot battle.
pub fn is_egg(&self) -> bool {
self.data.is_egg
}
pub fn is_egg(&self) -> bool { self.data.is_egg }
/// The stats of the Pokemon including the stat boosts
pub fn boosted_stats(&self) -> &Arc<StatisticSet<u32>> {
&self.data.boosted_stats
}
pub fn boosted_stats(&self) -> &Arc<StatisticSet<u32>> { &self.data.boosted_stats }
/// Get the stat boosts for a specific stat.
pub fn stat_boost(&self, stat: Statistic) -> i8 {
self.data.stat_boost.get_stat(stat)
}
pub fn stat_boost(&self, stat: Statistic) -> i8 { self.data.stat_boost.get_stat(stat) }
/// 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> {
let mut prevent = false;
@ -440,14 +386,10 @@ impl Pokemon {
/// Gets an individual value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Individual_values)
pub fn individual_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 31>> {
&self.data.individual_values
}
pub fn individual_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 31>> { &self.data.individual_values }
/// Gets an effort value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Effort_values)
pub fn effort_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 252>> {
&self.data.effort_values
}
pub fn effort_values(&self) -> &Arc<ClampedStatisticSet<u8, 0, 252>> { &self.data.effort_values }
/// Gets the battle the battle is currently in.
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
/// if the Pokemon is on the battlefield.
pub fn get_battle_index(&self) -> Option<u8> {
self.data.battle_data.read().as_ref().map(|data| data.index())
}
pub fn get_battle_index(&self) -> Option<u8> { self.data.battle_data.read().as_ref().map(|data| data.index()) }
/// Returns whether something overrides the ability.
pub fn is_ability_overriden(&self) -> bool {
self.data.override_ability.is_some()
}
pub fn is_ability_overridden(&self) -> bool { self.data.override_ability.is_some() }
/// Returns the currently active ability.
pub fn active_ability(&self) -> Result<Arc<dyn Ability>> {
if let Some(v) = &self.data.override_ability {
@ -489,32 +427,24 @@ impl Pokemon {
.library
.static_data()
.abilities()
.get(ability)
.get(&ability)
.ok_or(PkmnError::InvalidAbilityName {
ability: ability.clone(),
})?)
}
/// The script for the status.
pub fn status(&self) -> &ScriptContainer {
&self.data.status_script
}
pub fn status(&self) -> &ScriptContainer { &self.data.status_script }
/// Returns the script for the currently active ability.
pub fn ability_script(&self) -> &ScriptContainer {
&self.data.ability_script
}
pub fn ability_script(&self) -> &ScriptContainer { &self.data.ability_script }
/// Whether or not the Pokemon is allowed to gain experience.
pub fn allowed_experience_gain(&self) -> bool {
self.data.allowed_experience
}
pub fn allowed_experience_gain(&self) -> bool { self.data.allowed_experience }
/// The nature of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Nature)
pub fn nature(&self) -> &Arc<dyn Nature> {
&self.data.nature
}
pub fn nature(&self) -> &Arc<dyn Nature> { &self.data.nature }
/// 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
@ -606,7 +536,7 @@ impl Pokemon {
.set(ability_script)
.as_ref()
// 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 {
Ok(_) => (),
Err(e) => {
@ -646,14 +576,10 @@ impl Pokemon {
}
/// Whether or not the Pokemon is useable in a battle.
pub fn is_usable(&self) -> bool {
!self.data.is_caught && !self.data.is_egg && !self.is_fainted()
}
pub fn is_usable(&self) -> bool { !self.data.is_caught && !self.data.is_egg && !self.is_fainted() }
/// Returns whether the Pokemon is fainted.
pub fn is_fainted(&self) -> bool {
self.current_health() == 0
}
pub fn is_fainted(&self) -> bool { self.current_health() == 0 }
/// Sets the current battle the Pokemon is in.
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.
pub fn clear_status(&self) {
self.data.status_script.clear()
}
pub fn clear_status(&self) { self.data.status_script.clear() }
/// Increases the level by a certain amount
pub fn change_level_by(&self, amount: LevelInt) -> Result<()> {
@ -858,9 +782,7 @@ impl Pokemon {
/// Converts the Pokemon into a serializable form.
#[cfg(feature = "serde")]
pub fn serialize(&self) -> Result<super::serialization::SerializedPokemon> {
self.into()
}
pub fn serialize(&self) -> Result<super::serialization::SerializedPokemon> { self.into() }
/// Deserializes a Pokemon from a serializable form.
#[cfg(feature = "serde")]
@ -995,23 +917,17 @@ impl Pokemon {
}
/// Gets the inner pointer to the reference counted data.
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
pub fn as_ptr(&self) -> *const c_void { Arc::as_ptr(&self.data) as *const c_void }
}
impl PartialEq for Pokemon {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.data, &other.data) }
}
impl Eq for Pokemon {}
impl PartialEq for WeakPokemonReference {
fn eq(&self, other: &Self) -> bool {
Weak::ptr_eq(&self.data, &other.data)
}
fn eq(&self, other: &Self) -> bool { Weak::ptr_eq(&self.data, &other.data) }
}
impl Eq for WeakPokemonReference {}
@ -1025,9 +941,8 @@ impl WeakPokemonReference {
}
/// Gets the pointer to the underlying data.
pub(crate) fn as_ptr(&self) -> *const c_void {
self.data.as_ptr() as *const c_void
}
#[cfg(feature = "wasm")]
pub(crate) fn as_ptr(&self) -> *const c_void { self.data.as_ptr() as *const c_void }
}
/// The data of the Pokemon related to being in a battle.
@ -1047,26 +962,16 @@ pub struct PokemonBattleData {
impl PokemonBattleData {
/// The battle data of the Pokemon
pub fn battle(&self) -> Option<Battle> {
self.battle.upgrade()
}
pub fn battle(&self) -> Option<Battle> { self.battle.upgrade() }
/// The index of the side of the Pokemon
pub fn battle_side_index(&self) -> u8 {
self.battle_side_index.load(Ordering::Relaxed)
}
pub fn battle_side_index(&self) -> u8 { self.battle_side_index.load(Ordering::Relaxed) }
/// The index of the slot on the side of the Pokemon.
pub fn index(&self) -> u8 {
self.index.load(Ordering::Relaxed)
}
pub fn index(&self) -> u8 { self.index.load(Ordering::Relaxed) }
/// Whether or not the Pokemon is on the battlefield.
pub fn on_battle_field(&self) -> bool {
self.on_battle_field.load(Ordering::Relaxed)
}
pub fn on_battle_field(&self) -> bool { self.on_battle_field.load(Ordering::Relaxed) }
/// A list of opponents the Pokemon has seen this battle.
pub fn seen_opponents(&self) -> &RwLock<Vec<WeakPokemonReference>> {
&self.seen_opponents
}
pub fn seen_opponents(&self) -> &RwLock<Vec<WeakPokemonReference>> { &self.seen_opponents }
}
impl ScriptSource for Pokemon {
@ -1083,9 +988,7 @@ impl ScriptSource for Pokemon {
Ok(c)
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.data.script_source_data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.data.script_source_data }
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.data.held_item_trigger_script).into());
@ -1109,9 +1012,7 @@ impl ScriptSource for Pokemon {
}
impl VolatileScriptsOwner for Pokemon {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.data.volatile
}
fn volatile_scripts(&self) -> &Arc<ScriptSet> { &self.data.volatile }
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
self.data.library.load_script(self.into(), ScriptCategory::Pokemon, key)
@ -1120,6 +1021,7 @@ impl VolatileScriptsOwner for Pokemon {
/// A source of damage.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum DamageSource {
/// The damage is done by a move.

View File

@ -96,10 +96,7 @@ impl Into<anyhow_ext::Result<SerializedPokemon>> for &Pokemon {
personality_value: self.personality_value(),
gender: self.gender(),
coloring: self.coloring(),
held_item: {
let held_item = self.held_item().read();
held_item.as_ref().map(|held_item| held_item.name().clone())
},
held_item: self.held_item().map(|held_item| held_item.name().clone()),
current_health: self.current_health(),
weight: self.weight(),
height: self.height(),
@ -111,7 +108,7 @@ impl Into<anyhow_ext::Result<SerializedPokemon>> for &Pokemon {
nickname: self.nickname().clone(),
ability_index: *self.real_ability(),
override_ability: {
if self.is_ability_overriden() {
if self.is_ability_overridden() {
Some(self.active_ability()?.name().clone())
} else {
None

View File

@ -4,14 +4,10 @@ use std::sync::{Arc, LazyLock, Weak};
use parking_lot::RwLock;
use crate::VecExt;
#[doc(inline)]
pub use item_script::*;
#[doc(inline)]
pub use script::*;
#[doc(inline)]
pub use script_set::*;
#[doc(inline)]
pub use volatile_scripts_owner::*;
#[doc(inline)] pub use item_script::*;
#[doc(inline)] pub use script::*;
#[doc(inline)] pub use script_set::*;
#[doc(inline)] pub use volatile_scripts_owner::*;
/// Scripts that are used for item usage
mod item_script;
@ -169,15 +165,11 @@ pub enum ScriptWrapper {
}
impl From<&ScriptContainer> for ScriptWrapper {
fn from(c: &ScriptContainer) -> Self {
ScriptWrapper::Script(Arc::downgrade(c.arc()))
}
fn from(c: &ScriptContainer) -> Self { ScriptWrapper::Script(Arc::downgrade(c.arc())) }
}
impl From<&Arc<ScriptSet>> for ScriptWrapper {
fn from(c: &Arc<ScriptSet>) -> Self {
ScriptWrapper::Set(Arc::downgrade(c))
}
fn from(c: &Arc<ScriptSet>) -> Self { ScriptWrapper::Set(Arc::downgrade(c)) }
}
/// This struct allows for the iteration over scripts.
@ -291,7 +283,6 @@ mod tests {
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::dynamic_data::script_handling::script::ScriptContainer;
use crate::StringKey;
use super::*;
@ -323,17 +314,11 @@ mod tests {
}
impl Script for TestScript {
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn name(&self) -> Result<&StringKey> { Ok(&self.name) }
fn get_marked_for_deletion(&self) -> &AtomicBool {
&self.is_marked_for_deletion
}
fn get_marked_for_deletion(&self) -> &AtomicBool { &self.is_marked_for_deletion }
fn get_suppressed_count(&self) -> &AtomicUsize {
&self.suppressed_count
}
fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
fn add_suppression(&self) {}
@ -344,13 +329,9 @@ mod tests {
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
#[test]
@ -551,17 +532,11 @@ mod tests {
}
impl ScriptSource for TestScriptSource {
fn get_script_count(&self) -> Result<usize> {
Ok(1)
}
fn get_script_count(&self) -> Result<usize> { Ok(1) }
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.data
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> { &self.data }
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.script).into());
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) { scripts.push((&self.script).into()); }
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts);

View File

@ -1,4 +1,5 @@
use anyhow::{anyhow, Result};
use hashbrown::HashMap;
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
@ -30,97 +31,69 @@ pub trait Script: Send + Sync {
fn get_marked_for_deletion(&self) -> &AtomicBool;
/// This marks the script for deletion, which will dispose of it as soon as possible.
fn mark_for_deletion(&self) {
self.get_marked_for_deletion().store(true, Ordering::SeqCst);
}
fn mark_for_deletion(&self) { self.get_marked_for_deletion().store(true, Ordering::SeqCst); }
/// Helper function to get the value of the marked for deletion bool.
fn is_marked_for_deletion(&self) -> bool {
self.get_marked_for_deletion().load(Ordering::SeqCst)
}
fn is_marked_for_deletion(&self) -> bool { 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
/// we will not execute its methods. This should return the number of suppressions on the script.
fn get_suppressed_count(&self) -> &AtomicUsize;
/// Helper function to check if there is at least one suppression on the script
fn is_suppressed(&self) -> bool {
self.get_suppressed_count().load(Ordering::SeqCst) > 0
}
fn is_suppressed(&self) -> bool { self.get_suppressed_count().load(Ordering::SeqCst) > 0 }
/// Adds a suppression. This makes the script not run anymore. Note that adding this should also
/// remove the suppression later.
///
/// 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.
fn add_suppression(&self) {
self.get_suppressed_count().fetch_add(1, Ordering::SeqCst);
}
fn add_suppression(&self) { self.get_suppressed_count().fetch_add(1, Ordering::SeqCst); }
/// 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.
fn remove_suppression(&self) {
self.get_suppressed_count().fetch_sub(1, Ordering::SeqCst);
}
fn remove_suppression(&self) { 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
/// in place. Instead of adding the volatile effect twice, it will execute this function instead.
fn stack(&self) -> Result<()> {
Ok(())
}
fn stack(&self) -> Result<()> { Ok(()) }
/// This function is ran when this script stops being in effect, and is removed from its owner.
fn on_remove(&self) -> Result<()> {
Ok(())
}
fn on_remove(&self) -> Result<()> { Ok(()) }
/// 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(())
}
/// 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
/// something has happened during a turn.
fn on_before_turn(&self, _choice: &Arc<TurnChoice>) -> Result<()> {
Ok(())
}
fn on_before_turn(&self, _choice: &Arc<TurnChoice>) -> Result<()> { Ok(()) }
/// 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.
fn change_speed(&self, _choice: &Arc<TurnChoice>, _speed: &mut u32) -> Result<()> {
Ok(())
}
fn change_speed(&self, _choice: &Arc<TurnChoice>, _speed: &mut u32) -> Result<()> { Ok(()) }
/// 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
/// 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<()> {
Ok(())
}
fn change_priority(&self, _choice: &Arc<TurnChoice>, _priority: &mut i8) -> Result<()> { Ok(()) }
/// 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.
fn change_move(&self, _choice: &Arc<TurnChoice>, _move_name: &mut StringKey) -> Result<()> {
Ok(())
}
fn change_move(&self, _choice: &Arc<TurnChoice>, _move_name: &mut StringKey) -> Result<()> { Ok(()) }
/// 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
/// first hit.
fn change_number_of_hits(&self, _choice: &Arc<TurnChoice>, _number_of_hits: &mut u8) -> Result<()> {
Ok(())
}
fn change_number_of_hits(&self, _choice: &Arc<TurnChoice>, _number_of_hits: &mut u8) -> Result<()> { Ok(()) }
/// 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.
fn prevent_move(&self, _move: &Arc<ExecutingMove>, _prevent: &mut bool) -> Result<()> {
Ok(())
}
fn prevent_move(&self, _move: &Arc<ExecutingMove>, _prevent: &mut bool) -> Result<()> { Ok(()) }
/// This function makes the move fail. If the fail field gets set to true, the move ends execution,
/// and fail events get triggered.
fn fail_move(&self, _move: &Arc<ExecutingMove>, _fail: &mut bool) -> Result<()> {
Ok(())
}
fn fail_move(&self, _move: &Arc<ExecutingMove>, _fail: &mut bool) -> Result<()> { Ok(()) }
/// Similar to [`Self::prevent_move`]. This function will also stop execution, but PP will be
/// decreased.
fn stop_before_move(&self, _move: &Arc<ExecutingMove>, _stop: &mut bool) -> Result<()> {
Ok(())
}
fn stop_before_move(&self, _move: &Arc<ExecutingMove>, _stop: &mut bool) -> Result<()> { Ok(()) }
/// This function runs just before the move starts its execution.
fn on_before_move(&self, _move: &Arc<ExecutingMove>) -> Result<()> {
Ok(())
}
fn on_before_move(&self, _move: &Arc<ExecutingMove>) -> Result<()> { Ok(()) }
/// 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.
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
/// 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<()> {
Ok(())
}
fn on_move_miss(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { Ok(()) }
/// This function allows the script to change the actual type that is used for the move on a target.
fn change_move_type(
&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,
/// but before the secondary effect of the move happens.
fn on_incoming_hit(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> {
Ok(())
}
fn on_incoming_hit(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
/// This function triggers when an opponent on the field faints.
fn on_opponent_faints(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> {
Ok(())
}
fn on_opponent_faints(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
/// This function allows a script attached to a Pokemon or its parents to prevent stat boost
/// changes on that Pokemon.
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
/// 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.
fn on_secondary_effect(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> {
Ok(())
}
fn on_secondary_effect(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon, _hit: u8) -> Result<()> { Ok(()) }
/// 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<()> {
Ok(())
}
fn on_after_hits(&self, _move: &Arc<ExecutingMove>, _target: &Pokemon) -> Result<()> { Ok(()) }
/// 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<()> {
Ok(())
}
fn prevent_self_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
/// This function allows the prevention of switching for any opponent.
fn prevent_opponent_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> {
Ok(())
}
fn prevent_opponent_switch(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
/// This function is called on a move and its parents when the move fails.
fn on_fail(&self, _target: &Pokemon) -> Result<()> {
Ok(())
}
fn on_fail(&self, _target: &Pokemon) -> Result<()> { Ok(()) }
/// This function is called on a script when an opponent fails.
fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> {
Ok(())
}
fn on_opponent_fail(&self, _target: &Pokemon) -> Result<()> { Ok(()) }
/// 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<()> {
Ok(())
}
fn prevent_self_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
/// 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<()> {
Ok(())
}
fn prevent_opponent_run_away(&self, _choice: &Arc<TurnChoice>, _prevent: &mut bool) -> Result<()> { Ok(()) }
/// 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
/// function.
fn on_end_turn(&self) -> Result<()> {
Ok(())
}
fn on_end_turn(&self) -> Result<()> { Ok(()) }
/// 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<()> {
Ok(())
}
/// This function is triggered on a Pokemon and its parents when the given Pokemon faints.
fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> {
Ok(())
}
fn on_faint(&self, _pokemon: &Pokemon, _source: DamageSource) -> Result<()> { Ok(()) }
/// This function is triggered on a Pokemon and its parents when the given Pokemon is switched into
/// the battlefield.
fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> {
Ok(())
}
fn on_switch_in(&self, _pokemon: &Pokemon) -> Result<()> { Ok(()) }
/// This function is triggered on a Pokemon and its parents when the given Pokemon consumes the
/// held item it had.
fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &Arc<dyn Item>) -> Result<()> {
Ok(())
}
fn on_after_held_item_consume(&self, _pokemon: &Pokemon, _item: &Arc<dyn Item>) -> Result<()> { Ok(()) }
/// This function is triggered on a Pokemon and its parents when the given Pokemon gains experience,
/// and allows for changing this amount of experience.
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
/// weather, and allows for blocking the weather change.
fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> {
Ok(())
}
fn block_weather(&self, _battle: &Battle, _blocked: &mut bool) -> Result<()> { Ok(()) }
/// 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
/// example status effects that change capture rates.
@ -474,9 +415,7 @@ pub trait Script: Send + Sync {
}
impl Debug for dyn Script {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
Ok(())
}
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result { Ok(()) }
}
/// 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.
pub fn arc(&self) -> &ScriptHolder {
&self.script
}
pub fn arc(&self) -> &ScriptHolder { &self.script }
/// Whether or not the script is set.
pub fn is_any(&self) -> bool {
self.script.read().is_some()
}
pub fn is_any(&self) -> bool { self.script.read().is_some() }
/// Get the underlying script as the downcasted value.
pub fn get_as<T: 'static>(&self) -> Result<MappedRwLockReadGuard<T>> {
@ -619,7 +554,7 @@ impl Clone for ScriptContainer {
#[allow(clippy::unwrap_used)]
#[allow(clippy::indexing_slicing)]
mod tests {
use std::sync::atomic::{AtomicBool, AtomicPtr};
use std::sync::atomic::AtomicPtr;
use super::*;
@ -646,29 +581,19 @@ mod tests {
unsafe impl Send for TestScript {}
impl Script for TestScript {
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn name(&self) -> Result<&StringKey> { Ok(&self.name) }
fn get_marked_for_deletion(&self) -> &AtomicBool {
&self.marked_for_deletion
}
fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion }
fn get_suppressed_count(&self) -> &AtomicUsize {
&self.suppressed_count
}
fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
fn stack(&self) -> Result<()> {
unsafe { self.container.load(Ordering::Relaxed).as_ref().unwrap().clear() }
Ok(())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any(&self) -> &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
@ -726,24 +651,14 @@ mod tests {
unsafe impl Send for ReplaceTestScript {}
impl Script for ReplaceTestScript {
fn name(&self) -> Result<&StringKey> {
Ok(&self.name)
}
fn name(&self) -> Result<&StringKey> { Ok(&self.name) }
fn get_marked_for_deletion(&self) -> &AtomicBool {
&self.marked_for_deletion
}
fn get_marked_for_deletion(&self) -> &AtomicBool { &self.marked_for_deletion }
fn get_suppressed_count(&self) -> &AtomicUsize {
&self.suppressed_count
}
fn get_suppressed_count(&self) -> &AtomicUsize { &self.suppressed_count }
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn as_any(&self) -> &dyn Any { self }
fn as_any_mut(&mut self) -> &mut dyn Any { self }
}
#[test]
@ -793,19 +708,13 @@ pub enum ScriptOwnerData {
}
impl From<&Pokemon> for ScriptOwnerData {
fn from(p: &Pokemon) -> Self {
ScriptOwnerData::Pokemon(p.weak())
}
fn from(p: &Pokemon) -> Self { ScriptOwnerData::Pokemon(p.weak()) }
}
impl From<&BattleSide> for ScriptOwnerData {
fn from(p: &BattleSide) -> Self {
ScriptOwnerData::BattleSide(p.weak())
}
fn from(p: &BattleSide) -> Self { ScriptOwnerData::BattleSide(p.weak()) }
}
impl From<&Battle> for ScriptOwnerData {
fn from(p: &Battle) -> Self {
ScriptOwnerData::Battle(p.weak())
}
fn from(p: &Battle) -> Self { ScriptOwnerData::Battle(p.weak()) }
}

View File

@ -78,16 +78,12 @@ extern "C" fn pokemon_display_form(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<
/// The level of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Level)
#[no_mangle]
extern "C" fn pokemon_level(handle: FFIHandle<Pokemon>) -> LevelInt {
handle.from_ffi_handle().level()
}
extern "C" fn pokemon_level(handle: FFIHandle<Pokemon>) -> LevelInt { handle.from_ffi_handle().level() }
/// The experience of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Experience)
#[no_mangle]
extern "C" fn pokemon_experience(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().experience()
}
extern "C" fn pokemon_experience(handle: FFIHandle<Pokemon>) -> u32 { handle.from_ffi_handle().experience() }
/// The personality value of the Pokemon.
/// [See also](https://bulbapedia.bulbagarden.net/wiki/Personality_value)
@ -98,21 +94,17 @@ extern "C" fn pokemon_personality_value(handle: FFIHandle<Pokemon>) -> u32 {
/// The gender of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_gender(handle: FFIHandle<Pokemon>) -> Gender {
handle.from_ffi_handle().gender()
}
extern "C" fn pokemon_gender(handle: FFIHandle<Pokemon>) -> Gender { handle.from_ffi_handle().gender() }
/// The coloring of the Pokemon. If this is 1, the Pokemon is shiny, otherwise it is not. This can
/// also be used for other custom coloring schemes.
#[no_mangle]
extern "C" fn pokemon_coloring(handle: FFIHandle<Pokemon>) -> u8 {
handle.from_ffi_handle().coloring()
}
extern "C" fn pokemon_coloring(handle: FFIHandle<Pokemon>) -> u8 { handle.from_ffi_handle().coloring() }
/// Gets the held item of a Pokemon
#[no_mangle]
extern "C" fn pokemon_held_item(handle: FFIHandle<Pokemon>) -> FFIHandle<Arc<dyn Item>> {
if let Some(v) = handle.from_ffi_handle().held_item().read().as_ref() {
if let Some(v) = handle.from_ffi_handle().held_item() {
FFIHandle::get_handle(v.clone().into())
} else {
FFIHandle::none()
@ -160,27 +152,19 @@ extern "C" fn pokemon_consume_held_item(handle: FFIHandle<Pokemon>) -> FFIResult
/// The current health of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_current_health(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().current_health()
}
extern "C" fn pokemon_current_health(handle: FFIHandle<Pokemon>) -> u32 { handle.from_ffi_handle().current_health() }
/// The max health of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_max_health(handle: FFIHandle<Pokemon>) -> u32 {
handle.from_ffi_handle().max_health()
}
extern "C" fn pokemon_max_health(handle: FFIHandle<Pokemon>) -> u32 { handle.from_ffi_handle().max_health() }
/// The current weight of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_weight(handle: FFIHandle<Pokemon>) -> f32 {
handle.from_ffi_handle().weight()
}
extern "C" fn pokemon_weight(handle: FFIHandle<Pokemon>) -> f32 { handle.from_ffi_handle().weight() }
/// The current height of the Pokemon.
#[no_mangle]
extern "C" fn pokemon_height(handle: FFIHandle<Pokemon>) -> f32 {
handle.from_ffi_handle().height()
}
extern "C" fn pokemon_height(handle: FFIHandle<Pokemon>) -> f32 { handle.from_ffi_handle().height() }
/// An optional nickname of the Pokemon.
#[no_mangle]
@ -211,9 +195,7 @@ extern "C" fn pokemon_real_ability_index(handle: FFIHandle<Pokemon>) -> u8 {
/// The amount of types the Pokemon has.
#[no_mangle]
extern "C" fn pokemon_types_length(ptr: FFIHandle<Pokemon>) -> usize {
ptr.from_ffi_handle().types().len()
}
extern "C" fn pokemon_types_length(ptr: FFIHandle<Pokemon>) -> usize { ptr.from_ffi_handle().types().len() }
/// Gets a type of the Pokemon.
#[no_mangle]
@ -328,7 +310,7 @@ extern "C" fn pokemon_get_battle_index(handle: FFIHandle<Pokemon>) -> u8 {
/// Returns whether something overrides the ability.
#[no_mangle]
extern "C" fn pokemon_is_ability_overriden(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_ability_overriden())
u8::from(handle.from_ffi_handle().is_ability_overridden())
}
/// Returns the currently active ability.
@ -388,15 +370,11 @@ extern "C" fn pokemon_change_form(handle: FFIHandle<Pokemon>, form: FFIHandle<Ar
/// Whether or not the Pokemon is useable in a battle.
#[no_mangle]
extern "C" fn pokemon_is_usable(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_usable())
}
extern "C" fn pokemon_is_usable(handle: FFIHandle<Pokemon>) -> u8 { u8::from(handle.from_ffi_handle().is_usable()) }
/// Returns whether the Pokemon is fainted.
#[no_mangle]
extern "C" fn pokemon_is_fainted(handle: FFIHandle<Pokemon>) -> u8 {
u8::from(handle.from_ffi_handle().is_fainted())
}
extern "C" fn pokemon_is_fainted(handle: FFIHandle<Pokemon>) -> u8 { u8::from(handle.from_ffi_handle().is_fainted()) }
/// Whether or not the Pokemon is on the battlefield.
#[no_mangle]
@ -447,9 +425,7 @@ extern "C" fn pokemon_learn_move(
/// Removes the current non-volatile status from the Pokemon.
#[no_mangle]
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) {
handle.from_ffi_handle().clear_status()
}
extern "C" fn pokemon_clear_status(handle: FFIHandle<Pokemon>) { handle.from_ffi_handle().clear_status() }
/// Returns a serialized version of the Pokemon as xml.
#[cfg(feature = "serde")]

View File

@ -1,9 +1,10 @@
use crate::ffi::FFIHandle;
use crate::ffi::FromFFIHandle;
use crate::ffi::{FFIHandle, NonOwnedPtrString};
use crate::ffi::{FFIResult, OwnedPtrString};
use crate::static_data::{Ability, AbilityImpl, Parameter};
use crate::StringKey;
use anyhow::anyhow;
use hashbrown::HashMap;
use std::ffi::{c_char, CStr, CString};
use std::sync::Arc;
@ -12,13 +13,18 @@ use std::sync::Arc;
unsafe extern "C" fn ability_new(
name: *const c_char,
effect: *const c_char,
parameter_keys: *const NonOwnedPtrString,
parameters: *const FFIHandle<Arc<Parameter>>,
parameters_length: usize,
) -> 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 mut parameters_vec: Vec<Arc<Parameter>> = Vec::with_capacity(parameters_length);
for parameter in parameters {
parameters_vec.push(parameter.from_ffi_handle());
let mut parameters_map: HashMap<StringKey, Arc<Parameter>> = HashMap::with_capacity(parameters_length);
for (index, parameter) in parameters.iter().enumerate() {
parameters_map.insert(
CStr::from_ptr(parameter_keys[index]).into(),
parameter.from_ffi_handle(),
);
}
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")),
};
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()))
}
@ -62,9 +68,10 @@ unsafe extern "C" fn ability_parameter_length(ptr: FFIHandle<Arc<dyn Ability>>)
#[no_mangle]
unsafe extern "C" fn ability_parameter_get(
ptr: FFIHandle<Arc<dyn Ability>>,
index: usize,
name: NonOwnedPtrString,
) -> 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())
} else {
FFIHandle::none()

View File

@ -5,7 +5,7 @@ use crate::static_data::{
};
use crate::StringKey;
use anyhow::anyhow;
use hashbrown::HashSet;
use hashbrown::{HashMap, HashSet};
use std::ffi::{c_char, CStr, CString};
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(
chance: f32,
effect_name: NonOwnedPtrString,
parameter_keys: *const NonOwnedPtrString,
parameters: *mut FFIHandle<Arc<Parameter>>,
parameters_length: usize,
) -> 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 mut parameters = Vec::with_capacity(parameters_length);
for parameter in parameter_slice {
parameters.push(parameter.from_ffi_handle())
let mut parameters = HashMap::with_capacity(parameters_length);
for (index, parameter) in parameter_slice.iter().enumerate() {
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(
@ -146,9 +149,10 @@ unsafe extern "C" fn secondary_effect_parameter_length(ptr: FFIHandle<Arc<dyn Se
#[no_mangle]
unsafe extern "C" fn secondary_effect_parameter_get(
ptr: FFIHandle<Arc<dyn SecondaryEffect>>,
index: usize,
name: NonOwnedPtrString,
) -> 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())
} else {
FFIHandle::none()

View File

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

View File

@ -1,3 +1,7 @@
/// The WASM module handles loading dynamic scripts through WebAssembly.
#[cfg(feature = "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,130 @@
use rune::Hash;
mod script;
pub mod script_resolver;
mod wrappers;
#[derive(Debug, Default)]
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,143 @@
use crate::dynamic_data::{DynamicLibrary, ExecutingMove, Pokemon, Script, 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 rune::runtime::{RuntimeContext, VmError, VmResult};
use rune::{Unit, Value};
use std::convert::TryFrom;
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::Arc;
pub struct RuneScript {
name: StringKey,
state: Value,
/// 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,
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: Value,
script_type: Arc<RuneScriptType>,
runtime: Arc<RuntimeContext>,
unit: Arc<Unit>,
) -> Self {
Self {
name,
state: object,
marked_for_deletion: Default::default(),
suppressed_count: Default::default(),
script_type,
runtime,
unit,
}
}
pub(crate) fn get_state(&self) -> Value { self.state.clone() }
}
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 binding = self.state.clone().into_struct()?;
let state = binding.data_mut();
for par in pars {
let key = rune::alloc::string::String::try_from(par.0.str())?;
state.insert(key, parameter_to_rune_value(par.1.as_ref())?)?;
}
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_int_reference(*speed as i64)?;
let res = vm
.execute(
hash,
vec![
rune::to_value(self.state.clone())?,
rune::to_value(choice.wrap())?,
speed_handle.clone(),
],
)?
.complete();
if let VmResult::Err(e) = res {
return Err(anyhow::anyhow!("Error executing script: {}", e));
}
*speed = get_int_reference_value(speed_handle)? as u32;
}
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());
let block_critical_handle = wrap_bool_reference(*block_critical)?;
vm.execute(
hash,
vec![
self.state.clone(),
move_data.wrap(),
target.wrap(),
rune::to_value(hit)?,
block_critical_handle.clone(),
],
)?;
*block_critical = get_bool_reference_value(block_critical_handle)?;
}
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,193 @@
use crate::dynamic_data::{ItemScript, Script, ScriptCategory, ScriptOwnerData, ScriptResolver};
use crate::script_implementations::rune::script::RuneScript;
use crate::script_implementations::rune::wrappers::RuneWrapper;
use crate::script_implementations::rune::RuneScriptType;
use crate::static_data::Item;
use crate::StringKey;
use hashbrown::HashMap;
use rune::compile::meta::AssociatedKind;
use rune::compile::{ComponentRef, MetaError};
use rune::diagnostics::Diagnostic;
use rune::runtime::RuntimeContext;
use rune::{Context, Diagnostics, Options, Source, Sources, Unit};
use std::any::Any;
use std::convert::TryFrom;
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 mut o = rune::runtime::Object::new();
let owner_obj = match owner {
ScriptOwnerData::Pokemon(p) => Some(p.upgrade().unwrap().wrap()),
ScriptOwnerData::BattleSide(_) => None, //FIXME
ScriptOwnerData::Battle(_) => None, //FIXME
ScriptOwnerData::None => None,
};
if let Some(owner_obj) = owner_obj {
o.insert(rune::alloc::String::try_from("owner")?, owner_obj.into())?;
}
let state = rune::to_value(o)?;
let script = Arc::new(RuneScript::new(
script_key.clone(),
state,
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(());
}
#[allow(clippy::unwrap_used)] // We know that the first element exists
let mod_name = meta.item.iter().nth(0).unwrap();
let category = match get_mod_category(mod_name) {
Ok(value) => value,
Err(value) => return value,
};
#[allow(clippy::unwrap_used)] // We know that the last element exists
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,62 @@
use crate::dynamic_data::Battle;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use rune::{Any, Value};
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneBattle>()?;
module.function_meta(RuneBattle::library)?;
module.function_meta(RuneBattle::parties)?;
module.function_meta(RuneBattle::can_flee)?;
module.function_meta(RuneBattle::number_of_sides)?;
module.function_meta(RuneBattle::pokemon_per_side)?;
module.function_meta(RuneBattle::sides)?;
module.function_meta(RuneBattle::random)?;
module.function_meta(RuneBattle::has_ended)?;
module.function_meta(RuneBattle::current_turn)?;
module.function_meta(RuneBattle::get_pokemon)?;
module.function_meta(RuneBattle::set_weather)?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneBattle(Battle);
impl_rune_wrapper!(&Battle, RuneBattle);
impl RuneBattle {
#[rune::function]
fn library(&self) -> Value { self.0.library().wrap() }
#[rune::function]
fn parties(&self) -> Vec<Value> { self.0.parties().iter().map(|p| p.wrap()).collect() }
#[rune::function]
fn can_flee(&self) -> bool { self.0.can_flee() }
#[rune::function]
fn number_of_sides(&self) -> u8 { self.0.number_of_sides() }
#[rune::function]
fn pokemon_per_side(&self) -> u8 { self.0.pokemon_per_side() }
#[rune::function]
fn sides(&self) -> Vec<Value> { self.0.sides().iter().map(|s| s.wrap()).collect() }
#[rune::function]
fn random(&self) -> Value { self.0.random().wrap() }
#[rune::function]
fn has_ended(&self) -> bool { self.0.has_ended() }
#[rune::function]
fn current_turn(&self) -> u32 { self.0.current_turn() }
#[rune::function]
fn get_pokemon(&self, side: u8, index: u8) -> Option<Value> { self.0.get_pokemon(side, index).map(|v| v.wrap()) }
#[rune::function]
fn set_weather(&self, weather: Option<RuneStringKey>) -> anyhow::Result<()> {
self.0.set_weather(weather.map(|w| w.0))
}
}

View File

@ -0,0 +1,14 @@
use crate::dynamic_data::BattleParty;
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneBattleParty>()?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneBattleParty(pub Arc<BattleParty>);
impl_rune_wrapper!(&Arc<BattleParty>, RuneBattleParty);

View File

@ -0,0 +1,43 @@
use crate::dynamic_data::BattleRandom;
use crate::script_implementations::rune::wrappers::dynamic_data::executing_move::RuneExecutingMove;
use crate::script_implementations::rune::wrappers::dynamic_data::pokemon::RunePokemon;
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneBattleRandom>()?;
module.function_meta(RuneBattleRandom::get)?;
module.function_meta(RuneBattleRandom::get_max)?;
module.function_meta(RuneBattleRandom::get_between)?;
module.function_meta(RuneBattleRandom::effect_chance)?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneBattleRandom(pub(crate) Arc<BattleRandom>);
impl_rune_wrapper!(&Arc<BattleRandom>, RuneBattleRandom);
impl RuneBattleRandom {
#[rune::function]
fn get(&self) -> i32 { self.0.get().unwrap() }
#[rune::function]
fn get_max(&self, max: i32) -> i32 { self.0.get_max(max).unwrap() }
#[rune::function]
fn get_between(&self, min: i32, max: i32) -> i32 { self.0.get_between(min, max).unwrap() }
#[rune::function]
fn effect_chance(
&self,
chance: f32,
executing_move: &RuneExecutingMove,
target: RunePokemon,
hit_number: u8,
) -> anyhow::Result<bool> {
self.0.effect_chance(chance, &executing_move.0, &target.0, hit_number)
}
}

View File

@ -0,0 +1,16 @@
use crate::dynamic_data::BattleSide;
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use rune::Any;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneBattleSide>()?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneBattleSide(pub BattleSide);
impl_rune_wrapper!(&BattleSide, RuneBattleSide);
impl RuneBattleSide {}

View File

@ -0,0 +1,72 @@
use crate::dynamic_data::{ExecutingMove, HitData};
use crate::script_implementations::rune::wrappers::dynamic_data::pokemon::RunePokemon;
use crate::script_implementations::rune::wrappers::dynamic_data::resolve_script_data;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneExecutingMove>()?;
module.function_meta(RuneExecutingMove::target_count)?;
module.function_meta(RuneExecutingMove::number_of_hits)?;
module.function_meta(RuneExecutingMove::user)?;
module.function_meta(RuneExecutingMove::chosen_move)?;
module.function_meta(RuneExecutingMove::use_move)?;
module.function_meta(RuneExecutingMove::script)?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneExecutingMove(pub Arc<ExecutingMove>);
impl_rune_wrapper!(&Arc<ExecutingMove>, RuneExecutingMove);
impl RuneExecutingMove {
#[rune::function]
fn target_count(&self) -> usize { self.0.target_count() }
#[rune::function]
fn number_of_hits(&self) -> u8 { self.0.number_of_hits() }
#[rune::function]
fn user(&self) -> Value { self.0.user().wrap() }
#[rune::function]
fn chosen_move(&self) -> Value { self.0.chosen_move().wrap() }
#[rune::function]
fn use_move(&self) -> Value { self.0.use_move().wrap() }
#[rune::function]
fn script(&self) -> Option<Value> { resolve_script_data(self.0.script()) }
#[rune::function]
fn get_hit_data(&self, for_target: RunePokemon, hit: u8) -> anyhow::Result<Value> {
self.0.get_hit_data(&for_target.0, hit).map(|hit_data| hit_data.wrap())
}
}
#[derive(Debug, Clone, Any)]
pub struct RuneHitData(pub Arc<HitData>);
impl_rune_wrapper!(&Arc<HitData>, RuneHitData);
impl RuneHitData {
#[rune::function]
fn is_critical(&self) -> bool { self.0.is_critical() }
#[rune::function]
fn base_power(&self) -> u8 { self.0.base_power() }
#[rune::function]
fn effectiveness(&self) -> f32 { self.0.effectiveness() }
#[rune::function]
fn damage(&self) -> u32 { self.0.damage() }
#[rune::function]
fn move_type(&self) -> u8 { u8::from(self.0.move_type()) }
#[rune::function]
fn fail(&self) { self.0.fail() }
}

View File

@ -0,0 +1,49 @@
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneLearnedMove>()?;
module.function_meta(RuneLearnedMove::move_data)?;
module.function_meta(RuneLearnedMove::max_pp)?;
module.function_meta(RuneLearnedMove::max_pp_modification)?;
module.function_meta(RuneLearnedMove::remaining_pp)?;
module.function_meta(RuneLearnedMove::learn_method)?;
module.function_meta(RuneLearnedMove::try_use)?;
module.function_meta(RuneLearnedMove::restore_all_uses)?;
module.function_meta(RuneLearnedMove::restore_uses)?;
Ok(())
}
#[derive(Debug, Clone, Any)]
struct RuneLearnedMove(Arc<LearnedMove>);
impl_rune_wrapper!(&Arc<LearnedMove>, RuneLearnedMove);
impl RuneLearnedMove {
#[rune::function]
fn move_data(&self) -> Value { self.0.move_data().wrap() }
#[rune::function]
fn max_pp(&self) -> u8 { self.0.max_pp() }
#[rune::function]
fn max_pp_modification(&self) -> u8 { self.0.max_pp_modification() }
#[rune::function]
fn remaining_pp(&self) -> u8 { self.0.remaining_pp() }
#[rune::function]
fn learn_method(&self) -> MoveLearnMethod { self.0.learn_method() }
#[rune::function]
fn try_use(&self, amount: u8) -> bool { self.0.try_use(amount) }
#[rune::function]
fn restore_all_uses(&self) { self.0.restore_all_uses() }
#[rune::function]
fn restore_uses(&self, amount: u8) { self.0.restore_uses(amount) }
}

View File

@ -0,0 +1,70 @@
use crate::dynamic_data::{BattleStatCalculator, DynamicLibrary, MiscLibrary};
use crate::script_implementations::rune::wrappers::dynamic_data::pokemon::RunePokemon;
use crate::script_implementations::rune::wrappers::dynamic_data::turn_choice::RuneTurnChoice;
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use crate::script_implementations::rune::wrappers::static_data::libraries::static_data::RuneStaticData;
use crate::static_data::{Statistic, TimeOfDay};
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneBattleStatCalculator>()?;
module.function_meta(RuneBattleStatCalculator::calculate_flat_stat)?;
module.function_meta(RuneBattleStatCalculator::calculate_boosted_stat)?;
module.ty::<RuneMiscLibrary>()?;
module.function_meta(RuneMiscLibrary::can_flee)?;
module.function_meta(RuneMiscLibrary::time_of_day)?;
module.ty::<RuneDynamicLibrary>()?;
module.function_meta(RuneDynamicLibrary::battle_stat_calculator)?;
module.function_meta(RuneDynamicLibrary::misc_library)?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub struct RuneBattleStatCalculator(Arc<dyn BattleStatCalculator>);
impl_rune_wrapper!(&Arc<dyn BattleStatCalculator>, RuneBattleStatCalculator);
impl RuneBattleStatCalculator {
#[rune::function]
fn calculate_flat_stat(&self, pokemon: RunePokemon, stat: Statistic) -> anyhow::Result<u32> {
self.0.calculate_flat_stat(&pokemon.0, stat)
}
#[rune::function]
fn calculate_boosted_stat(&self, pokemon: RunePokemon, stat: Statistic) -> anyhow::Result<u32> {
self.0.calculate_boosted_stat(&pokemon.0, stat)
}
}
#[derive(Debug, Clone, Any)]
pub struct RuneMiscLibrary(Arc<dyn MiscLibrary>);
impl_rune_wrapper!(&Arc<dyn MiscLibrary>, RuneMiscLibrary);
impl RuneMiscLibrary {
#[rune::function]
fn can_flee(&self, choice: RuneTurnChoice) -> bool { self.0.can_flee(choice.get_turn_choice()) }
#[rune::function]
fn time_of_day(&self) -> TimeOfDay { self.0.time_of_day() }
}
#[derive(Debug, Clone, Any)]
pub struct RuneDynamicLibrary(Arc<dyn DynamicLibrary>);
impl_rune_wrapper!(&Arc<dyn DynamicLibrary>, RuneDynamicLibrary);
impl RuneDynamicLibrary {
#[rune::function]
fn battle_stat_calculator(&self) -> RuneBattleStatCalculator {
RuneBattleStatCalculator(self.0.stat_calculator().clone())
}
#[rune::function]
fn misc_library(&self) -> RuneMiscLibrary { RuneMiscLibrary(self.0.misc_library().clone()) }
#[rune::function]
fn static_data(&self) -> RuneStaticData { RuneStaticData(self.0.static_data().clone()) }
}

View File

@ -0,0 +1,38 @@
use crate::dynamic_data::ScriptContainer;
use crate::script_implementations::rune::script::RuneScript;
mod battle;
mod battle_party;
mod battle_random;
mod battle_side;
mod executing_move;
mod learned_move;
mod libraries;
mod pokemon;
mod turn_choice;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
executing_move::register(module)?;
pokemon::register(module)?;
turn_choice::register(module)?;
learned_move::register(module)?;
battle::register(module)?;
battle_random::register(module)?;
battle_party::register(module)?;
battle_side::register(module)?;
libraries::register(module)?;
Ok(())
}
fn resolve_script_data(container: &ScriptContainer) -> Option<rune::Value> {
container
.get()
.map(|v| {
v.read()
.as_ref()
.map(|v| v.clone().as_any().downcast_ref::<RuneScript>().map(|s| s.get_state()))
})
.flatten()
.flatten()
}

View File

@ -0,0 +1,226 @@
use crate::defines::LevelInt;
use crate::dynamic_data::{DamageSource, EventBatchId, Pokemon};
use crate::script_implementations::rune::wrappers::dynamic_data::resolve_script_data;
use crate::script_implementations::rune::wrappers::static_data::form::RuneForm;
use crate::script_implementations::rune::wrappers::static_data::item::RuneItem;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::{Gender, Statistic};
use rune::runtime::Value;
use rune::Any;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<DamageSource>()?;
module.ty::<RunePokemon>()?;
module.function_meta(RunePokemon::library)?;
module.function_meta(RunePokemon::species)?;
module.function_meta(RunePokemon::form)?;
module.function_meta(RunePokemon::has_different_display_species)?;
module.function_meta(RunePokemon::display_species)?;
module.function_meta(RunePokemon::has_different_display_form)?;
module.function_meta(RunePokemon::display_form)?;
module.function_meta(RunePokemon::level)?;
module.function_meta(RunePokemon::experience)?;
module.function_meta(RunePokemon::personality_value)?;
module.function_meta(RunePokemon::gender)?;
module.function_meta(RunePokemon::coloring)?;
module.function_meta(RunePokemon::held_item)?;
module.function_meta(RunePokemon::has_held_item)?;
module.function_meta(RunePokemon::set_held_item)?;
module.function_meta(RunePokemon::remove_held_item)?;
module.function_meta(RunePokemon::consume_held_item)?;
module.function_meta(RunePokemon::current_health)?;
module.function_meta(RunePokemon::max_health)?;
module.function_meta(RunePokemon::weight)?;
module.function_meta(RunePokemon::set_weight)?;
module.function_meta(RunePokemon::height)?;
module.function_meta(RunePokemon::happiness)?;
module.function_meta(RunePokemon::nickname)?;
module.function_meta(RunePokemon::real_ability)?;
module.function_meta(RunePokemon::types)?;
module.function_meta(RunePokemon::learned_moves)?;
module.function_meta(RunePokemon::flat_stats)?;
module.function_meta(RunePokemon::is_egg)?;
module.function_meta(RunePokemon::boosted_stats)?;
module.function_meta(RunePokemon::stat_boost)?;
module.function_meta(RunePokemon::change_stat_boost)?;
module.function_meta(RunePokemon::get_individual_value)?;
module.function_meta(RunePokemon::get_effort_value)?;
module.function_meta(RunePokemon::get_battle)?;
module.function_meta(RunePokemon::get_battle_side_index)?;
module.function_meta(RunePokemon::get_battle_index)?;
module.function_meta(RunePokemon::is_ability_overridden)?;
module.function_meta(RunePokemon::active_ability)?;
module.function_meta(RunePokemon::allowed_experience_gain)?;
module.function_meta(RunePokemon::nature)?;
module.function_meta(RunePokemon::change_form)?;
module.function_meta(RunePokemon::is_usable)?;
module.function_meta(RunePokemon::is_fainted)?;
module.function_meta(RunePokemon::damage)?;
module.function_meta(RunePokemon::heal)?;
module.function_meta(RunePokemon::status_script)?;
module.function_meta(RunePokemon::clear_status)?;
Ok(())
}
#[derive(Any, Clone, Debug)]
pub struct RunePokemon(pub Pokemon);
impl RunePokemon {
#[rune::function]
fn library(&self) -> Value { self.0.library().wrap() }
#[rune::function]
fn species(&self) -> Value { self.0.species().wrap() }
#[rune::function]
fn form(&self) -> Value { self.0.form().wrap() }
#[rune::function]
fn has_different_display_species(&self) -> bool { self.0.has_different_display_species() }
#[rune::function]
fn display_species(&self) -> Value { self.0.display_species().wrap() }
#[rune::function]
fn has_different_display_form(&self) -> bool { self.0.has_different_display_form() }
#[rune::function]
fn display_form(&self) -> Value { self.0.display_form().wrap() }
#[rune::function]
fn level(&self) -> LevelInt { self.0.level() }
#[rune::function]
fn experience(&self) -> u32 { self.0.experience() }
#[rune::function]
fn personality_value(&self) -> u32 { self.0.personality_value() }
#[rune::function]
fn gender(&self) -> Gender { self.0.gender() }
#[rune::function]
fn coloring(&self) -> u8 { self.0.coloring() }
#[rune::function]
fn held_item(&self) -> Option<Value> { self.0.held_item().map(|v| v.wrap()) }
#[rune::function]
fn has_held_item(&self, key: RuneStringKey) -> bool { self.0.has_held_item(&key.0) }
#[rune::function]
fn set_held_item(&mut self, key: RuneItem) -> Option<Value> { self.0.set_held_item(&key.0).map(|v| v.wrap()) }
#[rune::function]
fn remove_held_item(&mut self) -> Option<Value> { self.0.remove_held_item().map(|v| v.wrap()) }
#[rune::function]
fn consume_held_item(&mut self) -> anyhow::Result<bool> { self.0.consume_held_item() }
#[rune::function]
fn current_health(&self) -> u32 { self.0.current_health() }
#[rune::function]
fn max_health(&self) -> u32 { self.0.max_health() }
#[rune::function]
fn weight(&self) -> f32 { self.0.weight() }
#[rune::function]
fn set_weight(&mut self, value: f32) { self.0.set_weight(value); }
#[rune::function]
fn height(&self) -> f32 { self.0.height() }
#[rune::function]
fn happiness(&self) -> u8 { self.0.happiness() }
#[rune::function]
fn nickname(&self) -> Option<String> { self.0.nickname().clone() }
#[rune::function]
fn real_ability(&self) -> (bool, u8) { (self.0.real_ability().hidden, self.0.real_ability().index) }
#[rune::function]
fn types(&self) -> Vec<u8> { self.0.types().iter().map(|v| u8::from(*v)).collect() }
#[rune::function]
fn learned_moves(&self) -> Vec<Option<Value>> {
let l = self.0.learned_moves().read();
l.iter().map(|v| v.as_ref().map(|l| l.wrap())).collect()
}
#[rune::function]
fn flat_stats(&self) -> Value { self.0.flat_stats().wrap() }
#[rune::function]
fn is_egg(&self) -> bool { self.0.is_egg() }
#[rune::function]
fn boosted_stats(&self) -> Value { self.0.boosted_stats().wrap() }
#[rune::function]
fn stat_boost(&self, stat: Statistic) -> i8 { self.0.stat_boost(stat) }
#[rune::function]
fn change_stat_boost(&mut self, stat: Statistic, diff_amount: i8, self_inflicted: bool) -> anyhow::Result<bool> {
self.0.change_stat_boost(stat, diff_amount, self_inflicted)
}
#[rune::function]
fn get_individual_value(&self, stat: Statistic) -> u8 { self.0.individual_values().get_stat(stat) }
#[rune::function]
fn get_effort_value(&self, stat: Statistic) -> u8 { self.0.effort_values().get_stat(stat) }
#[rune::function]
fn get_battle(&self) -> Option<Value> { self.0.get_battle().map(|v| v.wrap()) }
#[rune::function]
fn get_battle_side_index(&self) -> Option<u8> { self.0.get_battle_side_index() }
#[rune::function]
fn get_battle_index(&self) -> Option<u8> { self.0.get_battle_index() }
#[rune::function]
fn is_ability_overridden(&self) -> bool { self.0.is_ability_overridden() }
#[rune::function]
fn active_ability(&self) -> anyhow::Result<Value> { self.0.active_ability().map(|v| v.wrap()) }
#[rune::function]
fn allowed_experience_gain(&self) -> bool { self.0.allowed_experience_gain() }
#[rune::function]
fn nature(&self) -> Value { self.0.nature().wrap() }
#[rune::function]
fn change_form(&self, form: RuneForm) -> anyhow::Result<()> { self.0.change_form(&form.0) }
#[rune::function]
fn is_usable(&self) -> bool { self.0.is_usable() }
#[rune::function]
fn is_fainted(&self) -> bool { self.0.is_fainted() }
#[rune::function]
fn damage(&self, amount: u32, source: DamageSource) -> anyhow::Result<()> {
self.0.damage(amount, source, EventBatchId::default())
}
#[rune::function]
fn heal(&self, amount: u32, allow_revive: bool) -> bool { self.0.heal(amount, allow_revive) }
#[rune::function]
fn clear_status(&self) { self.0.clear_status() }
#[rune::function]
fn status_script(&self) -> Option<Value> { resolve_script_data(&self.0.status()) }
#[rune::function]
fn ability_script(&self) -> Option<Value> { resolve_script_data(&self.0.ability_script()) }
}
impl_rune_wrapper!(&Pokemon, RunePokemon);

View File

@ -0,0 +1,112 @@
use crate::dynamic_data::TurnChoice;
use crate::script_implementations::rune::wrappers::RuneWrapper;
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneTurnChoice>()?;
module.function_meta(RuneTurnChoice::user)?;
module.function_meta(RuneTurnChoice::speed)?;
module.function_meta(RuneTurnChoice::has_failed)?;
module.function_meta(RuneTurnChoice::fail)?;
module.ty::<RuneMoveChoice>()?;
module.function_meta(RuneMoveChoice::used_move)?;
module.function_meta(RuneMoveChoice::target_side)?;
module.function_meta(RuneMoveChoice::target_index)?;
module.function_meta(RuneMoveChoice::priority)?;
module.function_meta(RuneMoveChoice::user)?;
module.ty::<RuneItemChoice>()?;
module.ty::<RuneSwitchChoice>()?;
module.ty::<RuneFleeChoice>()?;
module.ty::<RunePassChoice>()?;
Ok(())
}
#[derive(Debug, Clone, Any)]
pub enum RuneTurnChoice {
Move(#[rune(get)] RuneMoveChoice),
Item(#[rune(get)] RuneItemChoice),
Switch(#[rune(get)] RuneSwitchChoice),
Flee(#[rune(get)] RuneFleeChoice),
Pass(#[rune(get)] RunePassChoice),
}
#[derive(Debug, Clone, Any)]
pub struct RuneMoveChoice(Arc<TurnChoice>);
#[derive(Debug, Clone, Any)]
pub struct RuneItemChoice(Arc<TurnChoice>);
#[derive(Debug, Clone, Any)]
pub struct RuneSwitchChoice(Arc<TurnChoice>);
#[derive(Debug, Clone, Any)]
pub struct RuneFleeChoice(Arc<TurnChoice>);
#[derive(Debug, Clone, Any)]
pub struct RunePassChoice(Arc<TurnChoice>);
impl RuneTurnChoice {
pub fn get_turn_choice(&self) -> &Arc<TurnChoice> {
match self {
RuneTurnChoice::Move(m) => &m.0,
RuneTurnChoice::Item(i) => &i.0,
RuneTurnChoice::Switch(s) => &s.0,
RuneTurnChoice::Flee(f) => &f.0,
RuneTurnChoice::Pass(p) => &p.0,
}
}
#[rune::function]
fn user(&self) -> Value { self.get_turn_choice().user().wrap() }
#[rune::function]
fn speed(&self) -> u32 { self.get_turn_choice().speed() }
#[rune::function]
fn has_failed(&self) -> bool { self.get_turn_choice().has_failed() }
#[rune::function]
fn fail(&self) { self.get_turn_choice().fail() }
}
impl RuneWrapper for &Arc<TurnChoice> {
fn wrap(self) -> Value {
let o = match self.as_ref() {
TurnChoice::Move(_) => RuneTurnChoice::Move(RuneMoveChoice(self.clone())),
TurnChoice::Item(_) => RuneTurnChoice::Item(RuneItemChoice(self.clone())),
TurnChoice::Switch(_) => RuneTurnChoice::Switch(RuneSwitchChoice(self.clone())),
TurnChoice::Flee(_) => RuneTurnChoice::Flee(RuneFleeChoice(self.clone())),
TurnChoice::Pass(_) => RuneTurnChoice::Pass(RunePassChoice(self.clone())),
};
rune::to_value(o).unwrap()
}
}
impl RuneMoveChoice {
fn move_choice(&self) -> &crate::dynamic_data::MoveChoice {
match self.0.as_ref() {
TurnChoice::Move(m) => m,
_ => unreachable!("RuneMoveChoice should only be created with a MoveChoice"),
}
}
#[rune::function]
fn used_move(&self) -> Value { self.move_choice().used_move().wrap() }
#[rune::function]
fn target_side(&self) -> u8 { self.move_choice().target_side() }
#[rune::function]
fn target_index(&self) -> u8 { self.move_choice().target_index() }
#[rune::function]
fn priority(&self) -> i8 { self.move_choice().priority() }
#[rune::function]
fn user(&self) -> Value { self.move_choice().user().wrap() }
}

View File

@ -0,0 +1,664 @@
use rune::alloc::fmt::TryWrite;
use rune::runtime::{Protocol, VmResult};
use rune::{Any, Value};
use std::num::Saturating;
mod dynamic_data;
mod parameters;
mod static_data;
pub trait RuneWrapper {
fn wrap(self) -> Value;
}
pub fn module() -> anyhow::Result<rune::Module> {
let mut module = rune::Module::new();
module.ty::<RuneValueIntWrapper>()?;
module.function_meta(RuneValueIntWrapper::rn_as_int)?;
module.function_meta(RuneValueIntWrapper::set_value)?;
module.associated_function(Protocol::ADD_ASSIGN, RuneValueIntWrapper::add_assign)?;
module.associated_function(Protocol::SUB_ASSIGN, RuneValueIntWrapper::sub_assign)?;
module.associated_function(Protocol::DIV_ASSIGN, RuneValueIntWrapper::div_assign)?;
module.associated_function(Protocol::MUL_ASSIGN, RuneValueIntWrapper::mul_assign)?;
module.associated_function(Protocol::PARTIAL_EQ, RuneValueIntWrapper::partial_eq)?;
module.associated_function(Protocol::EQ, RuneValueIntWrapper::eq)?;
module.function_meta(RuneValueIntWrapper::string_display)?;
module.ty::<RuneValueBoolWrapper>()?;
module.function_meta(RuneValueBoolWrapper::rn_as_bool)?;
module.function_meta(RuneValueBoolWrapper::set_value)?;
module.associated_function(Protocol::PARTIAL_EQ, RuneValueBoolWrapper::eq)?;
module.associated_function(Protocol::EQ, RuneValueBoolWrapper::eq)?;
module.function_meta(RuneValueBoolWrapper::string_display)?;
module.ty::<RuneStringKey>()?;
module.function_meta(RuneStringKey::string_display)?;
module.associated_function(Protocol::PARTIAL_EQ, RuneStringKey::eq)?;
module.associated_function(Protocol::EQ, RuneStringKey::eq)?;
parameters::register(&mut module)?;
dynamic_data::register(&mut module)?;
static_data::register(&mut module)?;
Ok(module)
}
pub fn wrap_int_reference(value: i64) -> anyhow::Result<Value> { Ok(rune::to_value(RuneValueIntWrapper::new(value))?) }
pub fn wrap_bool_reference(value: bool) -> anyhow::Result<Value> {
Ok(rune::to_value(RuneValueBoolWrapper::new(value))?)
}
pub fn get_int_reference_value(value: Value) -> anyhow::Result<i64> {
let obj = match value.into_any::<RuneValueIntWrapper>() {
Ok(obj) => obj,
Err(_) => return Err(anyhow::anyhow!("Value is not a RuneValueIntWrapper")),
};
Ok(obj.as_int())
}
pub fn get_bool_reference_value(value: Value) -> anyhow::Result<bool> {
let obj = match value.into_any::<RuneValueBoolWrapper>() {
Ok(obj) => obj,
Err(_) => return Err(anyhow::anyhow!("Value is not a RuneValueBoolWrapper")),
};
Ok(obj.as_bool())
}
macro_rules! impl_rune_wrapper {
($t:ty, $wrapped_type:ident) => {
impl crate::script_implementations::rune::wrappers::RuneWrapper for $t {
fn wrap(self) -> rune::runtime::Value { rune::to_value($wrapped_type(self.clone())).unwrap() }
}
};
}
use crate::StringKey;
use impl_rune_wrapper;
#[derive(Any, Clone)]
#[rune(name = RefInt)]
struct RuneValueIntWrapper {
value: Saturating<i64>,
}
impl RuneValueIntWrapper {
pub fn new(value: i64) -> Self {
Self {
value: Saturating(value),
}
}
pub fn as_int(&self) -> i64 { self.value.0 }
#[rune::function(path = RuneValueIntWrapper::as_int)]
fn rn_as_int(&self) -> i64 { self.value.0 }
#[rune::function]
fn set_value(&mut self, value: i64) { self.value.0 = value; }
fn add_assign(&mut self, other: i64) { self.value += other; }
fn sub_assign(&mut self, other: i64) { self.value -= other; }
fn div_assign(&mut self, other: Value) {
match other.as_integer() {
Ok(other) => self.value /= other,
Err(_) => match other.as_float() {
Ok(other) => self.div_assign_f64(other),
Err(_) => (),
},
}
}
fn div_assign_f64(&mut self, other: f64) {
let v = (self.value.0 as f64 / other).floor();
if v > i64::MAX as f64 {
self.value = Saturating(i64::MAX);
} else if v < i64::MIN as f64 {
self.value = Saturating(i64::MIN);
} else {
self.value = Saturating(v as i64);
}
}
fn mul_assign(&mut self, other: Value) {
match other.as_integer() {
Ok(other) => self.value *= other,
Err(_) => match other.as_float() {
Ok(other) => self.mul_assign_f64(other),
Err(_) => (),
},
}
}
fn mul_assign_f64(&mut self, other: f64) {
let v = (self.value.0 as f64 * other).floor();
if v > i64::MAX as f64 {
self.value = Saturating(i64::MAX);
} else if v < i64::MIN as f64 {
self.value = Saturating(i64::MIN);
} else {
self.value = Saturating(v as i64);
}
}
fn partial_eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) }
fn eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) }
#[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.value.0);
VmResult::Ok(())
}
}
#[derive(Any, Clone)]
#[rune(name = RefBool)]
struct RuneValueBoolWrapper(bool);
impl RuneValueBoolWrapper {
pub fn new(value: bool) -> Self { Self(value) }
pub fn as_bool(&self) -> bool { self.0 }
#[rune::function(path = RuneValueIntWrapper::as_bool)]
fn rn_as_bool(&self) -> bool { self.0 }
#[rune::function]
fn set_value(&mut self, value: bool) { self.0 = value; }
#[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.0);
VmResult::Ok(())
}
fn eq(&self, b: bool) -> VmResult<bool> { VmResult::Ok(self.0 == b) }
}
#[derive(Any, Clone, Debug)]
#[rune(name = StringKey)]
pub(super) struct RuneStringKey(pub StringKey);
impl RuneStringKey {
pub fn new(value: StringKey) -> Self { Self(value) }
#[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.0);
VmResult::Ok(())
}
fn eq(&self, b: Value) -> VmResult<bool> {
match b.borrow_string_ref() {
Ok(s) => VmResult::Ok(self.0 == StringKey::new(&s)),
Err(_) => {
let b = b.borrow_any_ref::<RuneStringKey>();
match b {
Ok(b) => VmResult::Ok(self.0 == b.0),
_ => VmResult::Ok(false),
}
}
}
}
}
impl RuneWrapper for &StringKey {
fn wrap(self) -> Value { rune::to_value(RuneStringKey(self.clone())).unwrap() }
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
use rune::diagnostics::Diagnostic;
use rune::{Context, Diagnostics, Options, Source, Vm};
use std::sync::Arc;
pub fn setup_script(script: &str) -> Result<Vm> {
let mut context = Context::with_default_modules()?;
context.install(module()?)?;
let mut sources = rune::Sources::new();
sources.insert(Source::memory(script)?)?;
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 unit = rune::prepare(&mut sources)
.with_context(&context)
.with_diagnostics(&mut diagnostics)
.with_options(&options)
.build();
if !diagnostics.is_empty() && 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));
}
Ok(Vm::new(Arc::new(context.runtime()?), Arc::new(unit?)))
}
macro_rules! execute_vm {
($vm:expr, $func:expr, $val:expr) => {{
let args = vec![$val.clone()];
$vm.execute([$func], args)?.complete().into_result()?
}};
}
pub(crate) use execute_vm;
#[test]
fn test_int_wrapper_set_value() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a.set_value(5);
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 5);
Ok(())
}
#[test]
fn test_int_wrapper_as_int() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a.as_int()
}
"#,
)?;
let val = wrap_int_reference(10)?;
let res = execute_vm!(vm, "test_int", val);
let res = res.as_integer()?;
assert_eq!(res, 10);
Ok(())
}
#[test]
fn test_int_wrapper_add() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a += 5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 15);
Ok(())
}
#[test]
fn test_int_wrapper_add_overflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a += 5;
}
"#,
)?;
let val = wrap_int_reference(i64::MAX)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MAX);
Ok(())
}
#[test]
fn test_int_wrapper_sub() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a -= 5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 5);
Ok(())
}
#[test]
fn test_int_wrapper_underflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a -= 5;
}
"#,
)?;
let val = wrap_int_reference(i64::MIN)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MIN);
Ok(())
}
#[test]
fn test_int_wrapper_mul() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 50);
Ok(())
}
#[test]
fn test_int_wrapper_mul_float() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 0.5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 5);
Ok(())
}
#[test]
fn test_int_wrapper_mul_overflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 5;
}
"#,
)?;
let val = wrap_int_reference(i64::MAX)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MAX);
Ok(())
}
#[test]
fn test_int_wrapper_mul_float_overflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 10.0;
}
"#,
)?;
let val = wrap_int_reference(i64::MAX)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MAX);
Ok(())
}
#[test]
fn test_int_wrapper_mul_float_underflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 10.0;
}
"#,
)?;
let val = wrap_int_reference(i64::MIN)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MIN);
Ok(())
}
#[test]
fn test_int_wrapper_div() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 2);
Ok(())
}
#[test]
fn test_int_wrapper_div_float() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.5;
}
"#,
)?;
let val = wrap_int_reference(10)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, 20);
Ok(())
}
#[test]
fn test_int_wrapper_div_float_overflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.0001;
}
"#,
)?;
let val = wrap_int_reference(i64::MAX)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MAX);
Ok(())
}
#[test]
fn test_int_wrapper_div_float_underflow() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.0001;
}
"#,
)?;
let val = wrap_int_reference(i64::MIN)?;
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val)?;
assert_eq!(v, i64::MIN);
Ok(())
}
#[test]
fn test_int_wrapper_eq() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a == 5
}
"#,
)?;
let val = wrap_int_reference(5)?;
let res = execute_vm!(vm, "test_int", val);
let res = res.as_bool()?;
assert_eq!(res, true);
Ok(())
}
#[test]
fn test_int_wrapper_ineq() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a == 5
}
"#,
)?;
let val = wrap_int_reference(6)?;
let res = execute_vm!(vm, "test_int", val);
let res = res.as_bool()?;
assert_eq!(res, false);
Ok(())
}
#[test]
fn test_int_wrapper_string_display() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
`${a}`
}
"#,
)?;
let val = wrap_int_reference(5)?;
let res = execute_vm!(vm, "test_int", val);
let res = res.into_string()?.into_std();
assert_eq!(res, "5");
Ok(())
}
#[test]
fn test_bool_wrapper_set_value() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a.set_value(true);
}
"#,
)?;
let val = wrap_bool_reference(false)?;
execute_vm!(vm, "test_bool", val);
let v = get_bool_reference_value(val)?;
assert_eq!(v, true);
Ok(())
}
#[test]
fn test_bool_wrapper_as_bool() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a.as_bool()
}
"#,
)?;
let val = wrap_bool_reference(true)?;
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool()?;
assert_eq!(res, true);
Ok(())
}
#[test]
fn test_bool_wrapper_eq() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a == true
}
"#,
)?;
let val = wrap_bool_reference(true)?;
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool()?;
assert_eq!(res, true);
Ok(())
}
#[test]
fn test_bool_wrapper_ineq() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a == true
}
"#,
)?;
let val = wrap_bool_reference(false)?;
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool()?;
assert_eq!(res, false);
Ok(())
}
#[test]
fn test_bool_wrapper_string_display() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
`${a}`
}
"#,
)?;
let val = wrap_bool_reference(true)?;
let res = execute_vm!(vm, "test_bool", val);
let res = res.into_string()?.into_std();
assert_eq!(res, "true");
Ok(())
}
#[test]
fn test_string_key_wrapper_string_display() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_string_key(a) {
`${a}`
}
"#,
)?;
let val = StringKey::from("test");
let arg = val.wrap();
let res = execute_vm!(vm, "test_string_key", arg);
let res = res.into_string()?.into_std();
assert_eq!(res, "test");
Ok(())
}
#[test]
fn test_string_key_wrapper_eq() -> Result<()> {
let mut vm = setup_script(
r#"
pub fn test_string_key(a) {
a == "test"
}
"#,
)?;
let val = StringKey::from("test");
let arg = val.wrap();
let res = execute_vm!(vm, "test_string_key", arg);
let res = res.as_bool()?;
assert_eq!(res, true);
Ok(())
}
}

View File

@ -0,0 +1,32 @@
use crate::script_implementations::rune::wrappers::{RuneStringKey, RuneWrapper};
use crate::static_data::Parameter;
use rune::runtime::Value;
use rune::Any;
use std::ops::Deref;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneParameter>()?;
Ok(())
}
#[derive(Debug, Any)]
#[rune(name = Parameter)]
pub(super) enum RuneParameter {
Int(#[rune(get)] i32),
Float(#[rune(get)] f32),
String(#[rune(get)] RuneStringKey),
Bool(#[rune(get)] bool),
}
impl RuneWrapper for &Arc<Parameter> {
fn wrap(self) -> Value {
let p = match self.deref() {
Parameter::Bool(b) => RuneParameter::Bool(*b),
Parameter::Int(i) => RuneParameter::Int(*i as i32),
Parameter::Float(f) => RuneParameter::Float(*f),
Parameter::String(s) => RuneParameter::String(RuneStringKey::new(s.clone())),
};
rune::to_value(p).unwrap()
}
}

View File

@ -0,0 +1,120 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::Ability;
use crate::StringKey;
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneAbility>()?;
module.function_meta(RuneAbility::name)?;
module.function_meta(RuneAbility::effect)?;
module.function_meta(RuneAbility::get_parameter)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneAbility(Arc<dyn Ability>);
impl_rune_wrapper!(&Arc<dyn Ability>, RuneAbility);
impl RuneAbility {
#[rune::function]
fn name(&self) -> Value { self.0.name().wrap() }
#[rune::function]
fn effect(&self) -> Value { self.0.effect().wrap() }
#[rune::function]
fn get_parameter(&self, key: Value) -> Option<Value> {
if let Ok(s) = key.borrow_string_ref() {
return self.0.parameters().get(&StringKey::new(&s)).map(|v| v.wrap());
}
if let Ok(v) = key.into_any::<RuneStringKey>() {
self.0.parameters().get(&v.0).map(|v| v.wrap())
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::super::super::tests::{execute_vm, setup_script};
use super::*;
use crate::static_data::tests::MockAbility;
use crate::static_data::Parameter;
use hashbrown::HashMap;
#[test]
fn test_ability_name() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_ability(ability) {
let name = ability.name();
assert_eq!(name, "TestAbility");
}
"#,
)?;
let mut ability = MockAbility::new();
ability.expect_name().once().return_const(StringKey::new("TestAbility"));
let ability: Arc<dyn Ability> = Arc::new(ability);
let wrapped = ability.wrap();
execute_vm!(&mut vm, "test_ability", wrapped);
Ok(())
}
#[test]
fn test_ability_effect() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_ability(ability) {
let effect = ability.effect();
assert_eq!(effect, "TestEffect");
}
"#,
)?;
let mut ability = MockAbility::new();
ability
.expect_effect()
.once()
.return_const(StringKey::new("TestEffect"));
let ability: Arc<dyn Ability> = Arc::new(ability);
let wrapped = ability.wrap();
execute_vm!(&mut vm, "test_ability", wrapped);
Ok(())
}
#[test]
fn test_ability_get_parameter() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_ability(ability) {
let value = ability.get_parameter("test_key");
match value {
Some(v) => {
match v {
Parameter::String(s) => assert_eq!(s, "TestValue"),
_ => panic!("Invalid value type"),
}
}
None => panic!("Value not found"),
}
}
"#,
)?;
let mut ability = MockAbility::new();
ability.expect_parameters().once().return_const({
let mut map = HashMap::new();
map.insert(
StringKey::new("test_key"),
Arc::new(Parameter::String(StringKey::new("TestValue"))),
);
map
});
let a: Arc<dyn Ability> = Arc::new(ability);
let wrapped = a.wrap();
execute_vm!(&mut vm, "test_ability", wrapped);
Ok(())
}
}

View File

@ -0,0 +1,327 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::{AbilityIndex, Form, Statistic};
use crate::StringKey;
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneForm>()?;
module.function_meta(RuneForm::name)?;
module.function_meta(RuneForm::height)?;
module.function_meta(RuneForm::weight)?;
module.function_meta(RuneForm::base_experience)?;
module.function_meta(RuneForm::types)?;
module.function_meta(RuneForm::base_stats)?;
module.function_meta(RuneForm::abilities)?;
module.function_meta(RuneForm::hidden_abilities)?;
module.function_meta(RuneForm::has_flag)?;
module.function_meta(RuneForm::get_type)?;
module.function_meta(RuneForm::get_base_stat)?;
module.function_meta(RuneForm::get_ability)?;
Ok(())
}
#[derive(Debug, Any)]
#[rune(name = Form)]
pub struct RuneForm(pub Arc<dyn Form>);
impl_rune_wrapper!(&Arc<dyn Form>, RuneForm);
impl RuneForm {
#[rune::function]
fn name(&self) -> Value { self.0.name().wrap() }
#[rune::function]
fn height(&self) -> f32 { self.0.height() }
#[rune::function]
fn weight(&self) -> f32 { self.0.weight() }
#[rune::function]
fn base_experience(&self) -> u32 { self.0.base_experience() }
#[rune::function]
fn types(&self) -> Vec<i64> { self.0.types().iter().map(|t| u8::from(*t) as i64).collect() }
#[rune::function]
fn base_stats(&self) -> Value { self.0.base_stats().wrap() }
#[rune::function]
fn abilities(&self) -> Vec<Value> { self.0.abilities().iter().map(|a| a.wrap()).collect() }
#[rune::function]
fn hidden_abilities(&self) -> Vec<Value> { self.0.hidden_abilities().iter().map(|a| a.wrap()).collect() }
#[rune::function]
fn has_flag(&self, key: Value) -> bool {
if let Ok(s) = key.borrow_string_ref() {
return self.0.has_flag(&StringKey::new(&s));
}
if let Ok(v) = key.into_any::<RuneStringKey>() {
return self.0.has_flag(&v.0);
}
false
}
#[rune::function]
fn get_type(&self, index: usize) -> anyhow::Result<i64> { self.0.get_type(index).map(|t| u8::from(t) as i64) }
#[rune::function]
fn get_base_stat(&self, statistic: Statistic) -> u16 { self.0.get_base_stat(statistic) }
#[rune::function]
fn get_ability(&self, hidden: bool, index: i64) -> anyhow::Result<Value> {
if index > u8::MAX as i64 || index < 0 {
return Err(anyhow::anyhow!("Index out of bounds"));
}
let index = index as u8;
self.0.get_ability(AbilityIndex { hidden, index }).map(|a| a.wrap())
}
}
#[cfg(test)]
mod tests {
use super::super::super::tests::{execute_vm, setup_script};
use super::*;
use crate::static_data::tests::MockForm;
use crate::static_data::{StaticStatisticSet, TypeIdentifier};
#[test]
fn test_form_name() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let name = form.name();
assert_eq!(name, "TestForm");
}
"#,
)?;
let mut form = MockForm::new();
form.expect_name().once().return_const(StringKey::new("TestForm"));
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_height() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let height = form.height();
assert_eq!(height, 1.0);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_height().once().return_const(1.0);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_weight() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let weight = form.weight();
assert_eq!(weight, 1.0);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_weight().once().return_const(1.0);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_base_experience() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let base_experience = form.base_experience();
assert_eq!(base_experience, 1);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_base_experience().once().return_const(1u32);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_types() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let types = form.types();
assert_eq!(types[0], 1);
assert_eq!(types[1], 2);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_types()
.once()
.return_const(vec![TypeIdentifier::from(1u8), TypeIdentifier::from(2u8)]);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_base_stats() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let base_stats = form.base_stats();
assert_eq!(base_stats.hp(), 1);
assert_eq!(base_stats.attack(), 2);
assert_eq!(base_stats.defense(), 3);
assert_eq!(base_stats.special_attack(), 4);
assert_eq!(base_stats.special_defense(), 5);
assert_eq!(base_stats.speed(), 6);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_base_stats()
.once()
.return_const(Arc::new(StaticStatisticSet::<u16>::new(1, 2, 3, 4, 5, 6)));
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_abilities() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let abilities = form.abilities();
assert_eq!(abilities.len(), 1);
assert_eq!(abilities[0], "TestAbility");
}
"#,
)?;
let mut form = MockForm::new();
form.expect_abilities()
.once()
.return_const(vec![StringKey::new("TestAbility")]);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_hidden_abilities() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let abilities = form.hidden_abilities();
assert_eq!(abilities.len(), 1);
assert_eq!(abilities[0], "TestAbility");
}
"#,
)?;
let mut form = MockForm::new();
form.expect_hidden_abilities()
.once()
.return_const(vec![StringKey::new("TestAbility")]);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_has_flag() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let has_flag = form.has_flag("test_key");
assert_eq!(has_flag, true);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_has_flag().once().return_const(true);
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_get_type() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let type_id = form.get_type(0)?;
assert_eq!(type_id, 1);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_get_type()
.once()
.returning(|_| Ok(TypeIdentifier::from(1u8)));
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_get_base_stat() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let base_stat = form.get_base_stat(Statistic::HP);
assert_eq!(base_stat, 1);
}
"#,
)?;
let mut form = MockForm::new();
form.expect_get_base_stat()
.once()
.return_const(StaticStatisticSet::<u16>::new(1, 2, 3, 4, 5, 6).hp());
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
#[test]
fn test_form_get_ability() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_form(form) {
let ability = form.get_ability(false, 0)?;
assert_eq!(ability, "TestAbility");
}
"#,
)?;
let mut form = MockForm::new();
form.expect_get_ability()
.once()
.returning(move |_| Ok(StringKey::new("TestAbility")));
let form: Arc<dyn Form> = Arc::new(form);
let wrapped = form.wrap();
execute_vm!(&mut vm, "test_form", wrapped);
Ok(())
}
}

View File

@ -0,0 +1,79 @@
use crate::defines::LevelInt;
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use crate::static_data::GrowthRate;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneGrowthRate>()?;
module.function_meta(RuneGrowthRate::calculate_level)?;
module.function_meta(RuneGrowthRate::calculate_experience)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneGrowthRate(Arc<dyn GrowthRate>);
impl_rune_wrapper!(&Arc<dyn GrowthRate>, RuneGrowthRate);
impl RuneGrowthRate {
#[rune::function]
fn calculate_level(&self, experience: u32) -> i32 { self.0.calculate_level(experience) as i32 }
#[rune::function]
fn calculate_experience(&self, level: i64) -> Result<u32, String> {
if level < 0 {
return Err("Level cannot be negative".to_string());
}
if level > LevelInt::MAX as i64 {
return Err(format!("Level cannot be greater than {}", LevelInt::MAX));
}
let level = level as LevelInt;
self.0.calculate_experience(level).map_err(|e| e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::super::super::tests::{execute_vm, setup_script};
use super::*;
use crate::script_implementations::rune::wrappers::RuneWrapper;
use crate::static_data::tests::MockGrowthRate;
#[test]
fn test_calculate_level() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_growth_rate(growth_rate) {
let level = growth_rate.calculate_level(10);
assert_eq!(level, 10);
}
"#,
)?;
let mut growth_rate = MockGrowthRate::new();
growth_rate.expect_calculate_level().returning(|_| 10);
let growth_rate: Arc<dyn GrowthRate> = Arc::new(growth_rate);
let wrapped = growth_rate.wrap();
execute_vm!(&mut vm, "test_growth_rate", wrapped);
Ok(())
}
#[test]
fn test_calculate_experience() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_growth_rate(growth_rate) {
let experience = growth_rate.calculate_experience(10)?;
assert_eq!(experience, 10);
}
"#,
)?;
let mut growth_rate = MockGrowthRate::new();
growth_rate.expect_calculate_experience().returning(|_| Ok(10));
let growth_rate: Arc<dyn GrowthRate> = Arc::new(growth_rate);
let wrapped = growth_rate.wrap();
execute_vm!(&mut vm, "test_growth_rate", wrapped);
Ok(())
}
}

View File

@ -0,0 +1,73 @@
use std::sync::Arc;
use rune::runtime::Value;
use rune::Any;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::{BattleItemCategory, Item, ItemCategory};
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<ItemCategory>()?;
module.ty::<BattleItemCategory>()?;
module.ty::<RuneItem>()?;
module.function_meta(RuneItem::name)?;
module.function_meta(RuneItem::category)?;
module.function_meta(RuneItem::battle_category)?;
module.function_meta(RuneItem::price)?;
module.function_meta(RuneItem::flags)?;
module.function_meta(RuneItem::has_flag)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneItem(pub Arc<dyn Item>);
impl_rune_wrapper!(&Arc<dyn Item>, RuneItem);
impl RuneItem {
#[rune::function]
fn name(&self) -> Value { self.0.name().clone().wrap() }
#[rune::function]
fn category(&self) -> ItemCategory { self.0.category() }
#[rune::function]
fn battle_category(&self) -> BattleItemCategory { self.0.battle_category() }
#[rune::function]
fn price(&self) -> i32 { self.0.price() }
#[rune::function]
fn flags(&self) -> Vec<Value> { self.0.flags().iter().map(|s| s.clone().wrap()).collect() }
#[rune::function]
fn has_flag(&self, key: &RuneStringKey) -> bool { self.0.has_flag(&key.0) }
}
#[cfg(test)]
mod tests {
use super::super::super::tests::{execute_vm, setup_script};
use super::*;
use crate::static_data::tests::MockItem;
use crate::StringKey;
#[test]
fn test_get_name() -> anyhow::Result<()> {
let mut vm = setup_script(
r#"
pub fn test_item(item) {
let name = item.name();
assert_eq!(name, "Test Item");
}
"#,
)?;
let mut item = MockItem::new();
item.expect_name().once().return_const(StringKey::new("Test Item"));
let item: Arc<dyn Item> = Arc::new(item);
let wrapped = item.wrap();
execute_vm!(&mut vm, "test_item", wrapped);
Ok(())
}
}

View File

@ -0,0 +1,40 @@
use crate::defines::LevelInt;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::LearnableMoves;
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneLearnableMoves>()?;
module.function_meta(RuneLearnableMoves::get_learned_by_level)?;
module.function_meta(RuneLearnableMoves::get_distinct_level_moves)?;
module.function_meta(RuneLearnableMoves::learns_move)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneLearnableMoves(Arc<dyn LearnableMoves>);
impl_rune_wrapper!(&Arc<dyn LearnableMoves>, RuneLearnableMoves);
impl RuneLearnableMoves {
#[rune::function]
fn get_learned_by_level(&self, level: LevelInt) -> Option<Vec<Value>> {
self.0
.get_learned_by_level(level)
.map(|v| v.into_iter().map(|s| s.wrap()).collect())
}
#[rune::function]
fn get_distinct_level_moves(&self) -> Vec<Value> {
self.0
.get_distinct_level_moves()
.into_iter()
.map(|s| s.wrap())
.collect()
}
#[rune::function]
fn learns_move(&self, moveName: &RuneStringKey) -> bool { self.0.get_distinct_level_moves().contains(&moveName.0) }
}

View File

@ -0,0 +1,29 @@
use crate::defines::LevelInt;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey};
use crate::static_data::GrowthRateLibrary;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneGrowthRateLibrary>()?;
module.function_meta(RuneGrowthRateLibrary::calculate_level)?;
module.function_meta(RuneGrowthRateLibrary::calculate_experience)?;
Ok(())
}
#[derive(Debug, rune::Any)]
struct RuneGrowthRateLibrary(Arc<dyn GrowthRateLibrary>);
impl_rune_wrapper!(&Arc<dyn GrowthRateLibrary>, RuneGrowthRateLibrary);
impl RuneGrowthRateLibrary {
#[rune::function]
fn calculate_level(&self, growth_rate: &RuneStringKey, experience: u32) -> anyhow::Result<LevelInt> {
self.0.calculate_level(&growth_rate.0, experience)
}
#[rune::function]
fn calculate_experience(&self, growth_rate: &RuneStringKey, level: LevelInt) -> anyhow::Result<u32> {
self.0.calculate_experience(&growth_rate.0, level)
}
}

View File

@ -0,0 +1,23 @@
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use crate::static_data::LibrarySettings;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneLibrarySettings>()?;
module.function_meta(RuneLibrarySettings::maximum_level)?;
module.function_meta(RuneLibrarySettings::shiny_rate)?;
Ok(())
}
#[derive(Debug, rune::Any)]
struct RuneLibrarySettings(Arc<dyn LibrarySettings>);
impl_rune_wrapper!(&Arc<dyn LibrarySettings>, RuneLibrarySettings);
impl RuneLibrarySettings {
#[rune::function]
fn maximum_level(&self) -> i64 { self.0.maximum_level() as i64 }
#[rune::function]
fn shiny_rate(&self) -> i64 { self.0.shiny_rate() as i64 }
}

View File

@ -0,0 +1,68 @@
mod growth_rate_library;
mod library_settings;
mod nature_library;
pub mod static_data;
mod type_library;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::{AbilityLibrary, ItemLibrary, MoveLibrary, SpeciesLibrary};
use rune::runtime::Value;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneAbilityLibrary>()?;
module.function_meta(RuneAbilityLibrary::get)?;
module.function_meta(RuneAbilityLibrary::len)?;
module.function_meta(RuneAbilityLibrary::get_key_by_index)?;
module.ty::<RuneItemLibrary>()?;
module.function_meta(RuneItemLibrary::get)?;
module.function_meta(RuneItemLibrary::len)?;
module.function_meta(RuneItemLibrary::get_key_by_index)?;
module.ty::<RuneMoveLibrary>()?;
module.function_meta(RuneMoveLibrary::get)?;
module.function_meta(RuneMoveLibrary::len)?;
module.function_meta(RuneMoveLibrary::get_key_by_index)?;
module.ty::<RuneSpeciesLibrary>()?;
module.function_meta(RuneSpeciesLibrary::get)?;
module.function_meta(RuneSpeciesLibrary::len)?;
module.function_meta(RuneSpeciesLibrary::get_key_by_index)?;
growth_rate_library::register(module)?;
library_settings::register(module)?;
nature_library::register(module)?;
type_library::register(module)?;
static_data::register(module)?;
Ok(())
}
macro_rules! impl_rune_data_library_wrapper {
($t:ident, $wrapped_type:ty) => {
#[derive(Debug, rune::Any)]
struct $t($wrapped_type);
impl_rune_wrapper!(&$wrapped_type, $t);
impl $t {
#[rune::function]
fn get(&self, key: &RuneStringKey) -> Option<Value> { self.0.get(&key.0).map(|v| v.wrap()) }
#[rune::function]
fn len(&self) -> usize { self.0.len() }
#[rune::function]
fn get_key_by_index(&self, index: usize) -> Option<Value> {
self.0.get_key_by_index(index).map(|v| v.wrap())
}
}
};
}
impl_rune_data_library_wrapper!(RuneAbilityLibrary, Arc<dyn AbilityLibrary>);
impl_rune_data_library_wrapper!(RuneItemLibrary, Arc<dyn ItemLibrary>);
impl_rune_data_library_wrapper!(RuneMoveLibrary, Arc<dyn MoveLibrary>);
impl_rune_data_library_wrapper!(RuneSpeciesLibrary, Arc<dyn SpeciesLibrary>);

View File

@ -0,0 +1,27 @@
use crate::script_implementations::rune::wrappers::static_data::nature::RuneNature;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::NatureLibrary;
use rune::runtime::Value;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneNatureLibrary>()?;
module.function_meta(RuneNatureLibrary::get_nature)?;
module.function_meta(RuneNatureLibrary::get_nature_name)?;
Ok(())
}
#[derive(Debug, rune::Any)]
struct RuneNatureLibrary(Arc<dyn NatureLibrary>);
impl_rune_wrapper!(&Arc<dyn NatureLibrary>, RuneNatureLibrary);
impl RuneNatureLibrary {
#[rune::function]
fn get_nature(&self, key: RuneStringKey) -> Option<Value> { self.0.get_nature(&key.0).map(|v| v.wrap()) }
#[rune::function]
fn get_nature_name(&self, nature: &RuneNature) -> RuneStringKey {
RuneStringKey(self.0.get_nature_name(&nature.0).unwrap())
}
}

View File

@ -0,0 +1,48 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use crate::static_data::StaticData;
use rune::runtime::Value;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneStaticData>()?;
module.function_meta(RuneStaticData::settings)?;
module.function_meta(RuneStaticData::species)?;
module.function_meta(RuneStaticData::moves)?;
module.function_meta(RuneStaticData::items)?;
module.function_meta(RuneStaticData::growth_rates)?;
module.function_meta(RuneStaticData::types)?;
module.function_meta(RuneStaticData::natures)?;
module.function_meta(RuneStaticData::abilities)?;
Ok(())
}
#[derive(Debug, rune::Any)]
pub struct RuneStaticData(pub Arc<dyn StaticData>);
impl_rune_wrapper!(&Arc<dyn StaticData>, RuneStaticData);
impl RuneStaticData {
#[rune::function]
fn settings(&self) -> Value { self.0.settings().wrap() }
#[rune::function]
fn species(&self) -> Value { self.0.species().wrap() }
#[rune::function]
fn moves(&self) -> Value { self.0.moves().wrap() }
#[rune::function]
fn items(&self) -> Value { self.0.items().wrap() }
#[rune::function]
fn growth_rates(&self) -> Value { self.0.growth_rates().wrap() }
#[rune::function]
fn types(&self) -> Value { self.0.types().wrap() }
#[rune::function]
fn natures(&self) -> Value { self.0.natures().wrap() }
#[rune::function]
fn abilities(&self) -> Value { self.0.abilities().wrap() }
}

View File

@ -0,0 +1,39 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::TypeLibrary;
use rune::runtime::Value;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneTypeLibrary>()?;
module.function_meta(RuneTypeLibrary::get_type_id)?;
module.function_meta(RuneTypeLibrary::get_type_name)?;
module.function_meta(RuneTypeLibrary::get_single_effectiveness)?;
module.function_meta(RuneTypeLibrary::get_effectiveness)?;
Ok(())
}
#[derive(Debug, rune::Any)]
struct RuneTypeLibrary(Arc<dyn TypeLibrary>);
impl_rune_wrapper!(&Arc<dyn TypeLibrary>, RuneTypeLibrary);
impl RuneTypeLibrary {
#[rune::function]
fn get_type_id(&self, key: &RuneStringKey) -> Option<u8> { self.0.get_type_id(&key.0).map(|v| v.into()) }
#[rune::function]
fn get_type_name(&self, t: u8) -> Option<Value> { self.0.get_type_name(t.into()).map(|v| v.wrap()) }
#[rune::function]
fn get_single_effectiveness(&self, attacking: u8, defending: u8) -> anyhow::Result<f32> {
self.0.get_single_effectiveness(attacking.into(), defending.into())
}
#[rune::function]
fn get_effectiveness(&self, attacking: u8, defending: &[u8]) -> anyhow::Result<f32> {
self.0.get_effectiveness(
attacking.into(),
&defending.iter().map(|v| (*v).into()).collect::<Vec<_>>(),
)
}
}

View File

@ -0,0 +1,30 @@
mod ability;
pub mod form;
mod growth_rate;
pub mod item;
mod learnable_moves;
pub mod libraries;
mod move_data;
mod nature;
mod species;
mod statistic_set;
use crate::static_data::{Gender, Statistic, TimeOfDay};
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<TimeOfDay>()?;
module.ty::<Statistic>()?;
module.ty::<Gender>()?;
statistic_set::register(module)?;
nature::register(module)?;
item::register(module)?;
growth_rate::register(module)?;
form::register(module)?;
ability::register(module)?;
learnable_moves::register(module)?;
species::register(module)?;
move_data::register(module)?;
libraries::register(module)?;
Ok(())
}

View File

@ -0,0 +1,89 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect};
use rune::runtime::{Object, Value};
use rune::Any;
use std::convert::TryFrom;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<MoveCategory>()?;
module.ty::<MoveTarget>()?;
module.ty::<RuneMoveData>()?;
module.function_meta(RuneMoveData::name)?;
module.function_meta(RuneMoveData::move_type)?;
module.function_meta(RuneMoveData::category)?;
module.function_meta(RuneMoveData::base_power)?;
module.function_meta(RuneMoveData::accuracy)?;
module.function_meta(RuneMoveData::base_usages)?;
module.function_meta(RuneMoveData::target)?;
module.function_meta(RuneMoveData::priority)?;
module.function_meta(RuneMoveData::secondary_effect)?;
module.function_meta(RuneMoveData::has_flag)?;
module.ty::<RuneSecondaryEffect>()?;
module.function_meta(RuneSecondaryEffect::chance)?;
module.function_meta(RuneSecondaryEffect::effect_name)?;
module.function_meta(RuneSecondaryEffect::parameters)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneMoveData(Arc<dyn MoveData>);
impl_rune_wrapper!(&Arc<dyn MoveData>, RuneMoveData);
impl RuneMoveData {
#[rune::function]
fn name(&self) -> Value { self.0.name().wrap() }
#[rune::function]
fn move_type(&self) -> u8 { u8::from(self.0.move_type()) }
#[rune::function]
fn category(&self) -> MoveCategory { self.0.category() }
#[rune::function]
fn base_power(&self) -> u8 { self.0.base_power() }
#[rune::function]
fn accuracy(&self) -> u8 { self.0.accuracy() }
#[rune::function]
fn base_usages(&self) -> u8 { self.0.base_usages() }
#[rune::function]
fn target(&self) -> MoveTarget { self.0.target() }
#[rune::function]
fn priority(&self) -> i8 { self.0.priority() }
#[rune::function]
fn secondary_effect(&self) -> Option<Value> { self.0.secondary_effect().as_ref().map(|x| x.wrap()) }
#[rune::function]
fn has_flag(&self, flag: RuneStringKey) -> bool { self.0.has_flag(&flag.0) }
}
#[derive(Debug, Any)]
pub struct RuneSecondaryEffect(Arc<dyn SecondaryEffect>);
impl_rune_wrapper!(&Arc<dyn SecondaryEffect>, RuneSecondaryEffect);
impl RuneSecondaryEffect {
#[rune::function]
fn chance(&self) -> f32 { self.0.chance() }
#[rune::function]
fn effect_name(&self) -> Value { self.0.effect_name().wrap() }
#[rune::function]
fn parameters(&self) -> anyhow::Result<Object> {
let pars = self.0.parameters();
let mut o = Object::with_capacity(pars.len())?;
for (key, value) in pars.iter() {
o.insert(rune::alloc::String::try_from(key.str())?, Value::from(value.wrap()))?;
}
Ok(o)
}
}

View File

@ -0,0 +1,36 @@
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use crate::static_data::{Nature, Statistic};
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneNature>()?;
module.function_meta(RuneNature::increased_stat)?;
module.function_meta(RuneNature::decreased_stat)?;
module.function_meta(RuneNature::increased_modifier)?;
module.function_meta(RuneNature::decreased_modifier)?;
module.function_meta(RuneNature::get_stat_modifier)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneNature(pub Arc<dyn Nature>);
impl_rune_wrapper!(&Arc<dyn Nature>, RuneNature);
impl RuneNature {
#[rune::function]
fn increased_stat(&self) -> Statistic { self.0.increased_stat() }
#[rune::function]
fn decreased_stat(&self) -> Statistic { self.0.decreased_stat() }
#[rune::function]
fn increased_modifier(&self) -> f32 { self.0.increased_modifier() }
#[rune::function]
fn decreased_modifier(&self) -> f32 { self.0.decreased_modifier() }
#[rune::function]
fn get_stat_modifier(&self, stat: Statistic) -> f32 { self.0.get_stat_modifier(stat) }
}

View File

@ -0,0 +1,54 @@
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneStringKey, RuneWrapper};
use crate::static_data::Species;
use rune::runtime::Value;
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneSpecies>()?;
module.function_meta(RuneSpecies::id)?;
module.function_meta(RuneSpecies::name)?;
module.function_meta(RuneSpecies::gender_rate)?;
module.function_meta(RuneSpecies::growth_rate)?;
module.function_meta(RuneSpecies::capture_rate)?;
module.function_meta(RuneSpecies::base_happiness)?;
module.function_meta(RuneSpecies::get_form)?;
module.function_meta(RuneSpecies::get_default_form)?;
module.function_meta(RuneSpecies::has_flag)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneSpecies(Arc<dyn Species>);
impl_rune_wrapper!(&Arc<dyn Species>, RuneSpecies);
impl RuneSpecies {
#[rune::function]
fn id(&self) -> u16 { self.0.id() }
#[rune::function]
fn name(&self) -> Value { self.0.name().wrap() }
#[rune::function]
fn gender_rate(&self) -> f32 { self.0.gender_rate() }
#[rune::function]
fn growth_rate(&self) -> Value { self.0.growth_rate().wrap() }
#[rune::function]
fn capture_rate(&self) -> u8 { self.0.capture_rate() }
#[rune::function]
fn base_happiness(&self) -> u8 { self.0.base_happiness() }
#[rune::function]
fn get_form(&self, name: &RuneStringKey) -> Option<Value> { self.0.get_form(&name.0).map(|form| form.wrap()) }
#[rune::function]
fn get_default_form(&self) -> anyhow::Result<Value> { self.0.get_default_form().map(|v| v.wrap()) }
#[rune::function]
fn has_flag(&self, key: &RuneStringKey) -> bool { self.0.has_flag(&key.0) }
}

View File

@ -0,0 +1,85 @@
use crate::script_implementations::rune::wrappers::impl_rune_wrapper;
use crate::static_data::{StaticStatisticSet, Statistic, StatisticSet};
use rune::Any;
use std::sync::Arc;
pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.ty::<RuneU32StatisticSet>()?;
module.function_meta(RuneU32StatisticSet::get)?;
module.function_meta(RuneU32StatisticSet::set)?;
module.function_meta(RuneU32StatisticSet::hp)?;
module.function_meta(RuneU32StatisticSet::attack)?;
module.function_meta(RuneU32StatisticSet::defense)?;
module.function_meta(RuneU32StatisticSet::special_attack)?;
module.function_meta(RuneU32StatisticSet::special_defense)?;
module.function_meta(RuneU32StatisticSet::speed)?;
module.ty::<RuneStaticStatisticSet>()?;
module.function_meta(RuneStaticStatisticSet::get)?;
module.function_meta(RuneStaticStatisticSet::hp)?;
module.function_meta(RuneStaticStatisticSet::attack)?;
module.function_meta(RuneStaticStatisticSet::defense)?;
module.function_meta(RuneStaticStatisticSet::special_attack)?;
module.function_meta(RuneStaticStatisticSet::special_defense)?;
module.function_meta(RuneStaticStatisticSet::speed)?;
Ok(())
}
#[derive(Debug, Any)]
pub struct RuneU32StatisticSet(Arc<StatisticSet<u32>>);
impl_rune_wrapper!(&Arc<StatisticSet<u32>>, RuneU32StatisticSet);
impl RuneU32StatisticSet {
#[rune::function]
fn get(&self, stat: Statistic) -> u32 { self.0.get_stat(stat) }
#[rune::function]
fn set(&mut self, stat: Statistic, value: u32) { self.0.set_stat(stat, value) }
#[rune::function]
fn hp(&self) -> u32 { self.0.hp() }
#[rune::function]
fn attack(&self) -> u32 { self.0.attack() }
#[rune::function]
fn defense(&self) -> u32 { self.0.defense() }
#[rune::function]
fn special_attack(&self) -> u32 { self.0.special_attack() }
#[rune::function]
fn special_defense(&self) -> u32 { self.0.special_defense() }
#[rune::function]
fn speed(&self) -> u32 { self.0.speed() }
}
#[derive(Debug, Any)]
pub struct RuneStaticStatisticSet(Arc<StaticStatisticSet<u16>>);
impl_rune_wrapper!(&Arc<StaticStatisticSet<u16>>, RuneStaticStatisticSet);
impl RuneStaticStatisticSet {
#[rune::function]
fn get(&self, stat: Statistic) -> u16 { self.0.get_stat(stat) }
#[rune::function]
fn hp(&self) -> u16 { self.0.hp() }
#[rune::function]
fn attack(&self) -> u16 { self.0.attack() }
#[rune::function]
fn defense(&self) -> u16 { self.0.defense() }
#[rune::function]
fn special_attack(&self) -> u16 { self.0.special_attack() }
#[rune::function]
fn special_defense(&self) -> u16 { self.0.special_defense() }
#[rune::function]
pub fn speed(&self) -> u16 { self.0.speed() }
}

View File

@ -2,9 +2,10 @@ use crate::defines::LevelInt;
use crate::VecExt;
use anyhow::Result;
use anyhow_ext::ensure;
use std::fmt::Debug;
/// A growth rate defines how much experience is required per level.
pub trait GrowthRate {
pub trait GrowthRate: Debug {
/// Calculate the level something with this growth rate would have at a certain experience.
fn calculate_level(&self, experience: u32) -> LevelInt;
/// Calculate the experience something with this growth rate would have at a certain level.
@ -12,6 +13,7 @@ pub trait GrowthRate {
}
/// An implementation of the growth rate that uses a lookup table for experience.
#[derive(Debug)]
pub struct LookupGrowthRate {
/// The lookup Vec.
experience: Vec<u32>,
@ -20,9 +22,7 @@ pub struct LookupGrowthRate {
impl LookupGrowthRate {
/// Instantiates a new lookup growth rate. The experience vec should be the amount of experience
/// required per level, with the first element being the experience required for level 1 (generally 0).
pub fn new(experience: Vec<u32>) -> LookupGrowthRate {
LookupGrowthRate { experience }
}
pub fn new(experience: Vec<u32>) -> LookupGrowthRate { LookupGrowthRate { experience } }
}
impl GrowthRate for LookupGrowthRate {

View File

@ -1,6 +1,5 @@
use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")] use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt::Debug;
@ -9,40 +8,55 @@ use crate::StringKey;
/// An item category defines which bag slot items are stored in.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum ItemCategory {
/// This is where most items should go.
#[cfg_attr(feature = "rune", rune(constructor))]
MiscItem,
/// Pokeballs are used for capturing Pokemons.
#[cfg_attr(feature = "rune", rune(constructor))]
Pokeball,
/// Medicine is used for healing HP, PP, and status effects
#[cfg_attr(feature = "rune", rune(constructor))]
Medicine,
/// Berry is used for all berries.
#[cfg_attr(feature = "rune", rune(constructor))]
Berry,
/// TMHM is used for Technical and Hidden Machines.
#[cfg_attr(feature = "rune", rune(constructor))]
TMHM,
/// Form Changer is used for items that change forms, such as mega stones.
#[cfg_attr(feature = "rune", rune(constructor))]
FormChanger,
/// Key Items are single stored items, generally used for story progression.
#[cfg_attr(feature = "rune", rune(constructor))]
KeyItem,
/// Mail is used for mail items.
#[cfg_attr(feature = "rune", rune(constructor))]
Mail,
}
/// A battle item category defines how the item is categorized when in battle.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum BattleItemCategory {
/// This item can't be used in battle.
#[cfg_attr(feature = "rune", rune(constructor))]
None,
/// This item is used for healing Pokemon.
#[cfg_attr(feature = "rune", rune(constructor))]
Healing,
/// This item is used for healing Pokemon from a status.
#[cfg_attr(feature = "rune", rune(constructor))]
StatusHealing,
/// This item is used for capturing Pokemon.
#[cfg_attr(feature = "rune", rune(constructor))]
Pokeball,
/// This item does not belong in above categories, but is still a battle item.
#[cfg_attr(feature = "rune", rune(constructor))]
MiscBattleItem,
}
@ -99,30 +113,18 @@ impl ItemImpl {
impl Item for ItemImpl {
/// The name of the item.
fn name(&self) -> &StringKey {
&self.name
}
fn name(&self) -> &StringKey { &self.name }
/// Which bag slot items are stored in.
fn category(&self) -> ItemCategory {
self.category
}
fn category(&self) -> ItemCategory { self.category }
/// How the item is categorized when in battle.
fn battle_category(&self) -> BattleItemCategory {
self.battle_category
}
fn battle_category(&self) -> BattleItemCategory { self.battle_category }
/// The buying value of the item.
fn price(&self) -> i32 {
self.price
}
fn price(&self) -> i32 { self.price }
/// A set of arbitrary flags that can be set on the item.
fn flags(&self) -> &HashSet<StringKey> {
&self.flags
}
fn flags(&self) -> &HashSet<StringKey> { &self.flags }
/// Checks whether the item has a specific flag.
fn has_flag(&self, key: &StringKey) -> bool {
self.flags.contains(key)
}
fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) }
}
#[cfg(test)]

View File

@ -29,12 +29,8 @@ impl AbilityLibraryImpl {
impl AbilityLibrary for AbilityLibraryImpl {}
impl DataLibrary<dyn Ability> for AbilityLibraryImpl {
fn map(&self) -> &IndexMap<StringKey, Arc<dyn Ability>> {
&self.map
}
fn get_modify(&mut self) -> &mut IndexMap<StringKey, Arc<dyn Ability>> {
&mut self.map
}
fn map(&self) -> &IndexMap<StringKey, Arc<dyn Ability>> { &self.map }
fn get_modify(&mut self) -> &mut IndexMap<StringKey, Arc<dyn Ability>> { &mut self.map }
}
#[cfg(test)]
@ -45,6 +41,7 @@ pub mod tests {
use crate::static_data::AbilityImpl;
use crate::static_data::DataLibrary;
use crate::StringKey;
use hashbrown::HashMap;
use std::sync::Arc;
pub fn build() -> AbilityLibraryImpl {
@ -54,7 +51,7 @@ pub mod tests {
Arc::new(AbilityImpl::new(
&"test_ability".into(),
&"test_ability".into(),
Vec::new(),
HashMap::new(),
)),
);
lib

View File

@ -1,4 +1,5 @@
use anyhow_ext::Result;
use std::fmt::Debug;
use std::sync::Arc;
use indexmap::IndexMap;
@ -8,7 +9,7 @@ use crate::StringKey;
/// A data library is a collection of methods to set up a default library, where values are stored
/// by both key, while keeping their insertion order.
pub trait DataLibrary<T: ?Sized> {
pub trait DataLibrary<T: ?Sized>: Debug {
/// Returns the underlying map.
fn map(&self) -> &IndexMap<StringKey, Arc<T>>;
/// Returns the underlying map in mutable manner.
@ -25,32 +26,22 @@ pub trait DataLibrary<T: ?Sized> {
fn remove(&self, key: &StringKey) {
#[allow(clippy::unwrap_used)] // We know this cant fail.
let self_mut = unsafe { (self as *const Self as *mut Self).as_mut() }.unwrap();
self_mut.get_modify().remove(key);
self_mut.get_modify().swap_remove(key);
}
/// Gets a value from the library.
fn get(&self, key: &StringKey) -> Option<Arc<T>> {
self.map().get::<StringKey>(key).cloned()
}
fn get(&self, key: &StringKey) -> Option<Arc<T>> { self.map().get::<StringKey>(key).cloned() }
/// Gets a value from the library.
fn get_by_hash(&self, key: u32) -> Option<Arc<T>> {
self.map().get::<u32>(&key).cloned()
}
fn get_by_hash(&self, key: u32) -> Option<Arc<T>> { self.map().get::<u32>(&key).cloned() }
/// Gets a value from the library by the index where it is stored.
fn get_key_by_index(&self, index: usize) -> Option<StringKey> {
self.map().get_index(index).map(|a| a.0.clone())
}
fn get_key_by_index(&self, index: usize) -> Option<StringKey> { self.map().get_index(index).map(|a| a.0.clone()) }
/// Gets the amount of values in the library.
fn len(&self) -> usize {
self.map().len()
}
fn len(&self) -> usize { self.map().len() }
/// Returns whether the library has no values.
fn is_empty(&self) -> bool {
self.map().is_empty()
}
fn is_empty(&self) -> bool { self.map().is_empty() }
/// Gets a random value from the library.
fn random_value(&self, rand: &mut Random) -> Result<&Arc<T>> {

View File

@ -61,9 +61,7 @@ impl GrowthRateLibrary for GrowthRateLibraryImpl {
}
impl Debug for GrowthRateLibraryImpl {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("GrowthRateLibrary").finish()
}
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("GrowthRateLibrary").finish() }
}
#[cfg(test)]
@ -72,8 +70,6 @@ impl Debug for GrowthRateLibraryImpl {
pub mod tests {
use super::*;
use crate::static_data::growth_rates::LookupGrowthRate;
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
use crate::static_data::GrowthRateLibraryImpl;
pub fn build() -> GrowthRateLibraryImpl {
let mut lib = GrowthRateLibraryImpl::new(1);

View File

@ -38,14 +38,10 @@ impl NatureLibraryImpl {
impl NatureLibrary for NatureLibraryImpl {
/// Adds a new nature with name to the library.
fn load_nature(&self, name: StringKey, nature: Arc<dyn Nature>) {
self.map.write().insert(name, nature);
}
fn load_nature(&self, name: StringKey, nature: Arc<dyn Nature>) { self.map.write().insert(name, nature); }
/// Gets a nature by name.
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>> {
self.map.read().get(key).cloned()
}
fn get_nature(&self, key: &StringKey) -> Option<Arc<dyn Nature>> { self.map.read().get(key).cloned() }
fn get_random_nature(&self, rand: &mut Random) -> Result<Arc<dyn Nature>> {
let map = self.map.read();
@ -80,7 +76,7 @@ impl NatureLibrary for NatureLibraryImpl {
pub mod tests {
use super::*;
use crate::static_data::statistics::Statistic;
use crate::static_data::{NatureImpl, NatureLibrary, NatureLibraryImpl};
use crate::static_data::NatureImpl;
pub fn build() -> NatureLibraryImpl {
let lib = NatureLibraryImpl::new(2);

View File

@ -11,27 +11,18 @@ use crate::{PkmnError, StringKey};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Atom)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(transparent)]
pub struct TypeIdentifier {
/// The unique internal value.
val: u8,
}
pub struct TypeIdentifier(u8);
impl From<u8> for TypeIdentifier {
fn from(val: u8) -> Self {
Self { val }
}
fn from(val: u8) -> Self { Self(val) }
}
impl From<TypeIdentifier> for u8 {
fn from(id: TypeIdentifier) -> Self {
id.val
}
fn from(id: TypeIdentifier) -> Self { id.0 }
}
impl Display for TypeIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TypeId({})", self.val)
}
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "TypeId({})", self.0) }
}
/// All data related to types and effectiveness.
@ -87,18 +78,16 @@ impl TypeLibraryImpl {
defending: TypeIdentifier,
) -> Result<f32> {
Ok(*lock
.get((attacking.val - 1) as usize)
.get((attacking.0 - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get((defending.val - 1) as usize)
.get((defending.0 - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })?)
}
}
impl TypeLibrary for TypeLibraryImpl {
/// Gets the type identifier for a type with a name.
fn get_type_id(&self, key: &StringKey) -> Option<TypeIdentifier> {
self.types.read().get(key).cloned()
}
fn get_type_id(&self, key: &StringKey) -> Option<TypeIdentifier> { self.types.read().get(key).cloned() }
/// Gets the type name from the type identifier.
fn get_type_name(&self, t: TypeIdentifier) -> Option<StringKey> {
@ -133,13 +122,11 @@ impl TypeLibrary for TypeLibraryImpl {
let mut types_write_lock = self.types.write();
let mut effectiveness_write_lock = self.effectiveness.write();
let id = TypeIdentifier {
val: (types_write_lock.len() + 1) as u8,
};
let id = TypeIdentifier((types_write_lock.len() + 1) as u8);
types_write_lock.insert(name.clone(), id);
effectiveness_write_lock.resize((id.val) as usize, vec![]);
effectiveness_write_lock.resize((id.0) as usize, vec![]);
for effectiveness in &mut effectiveness_write_lock.iter_mut() {
effectiveness.resize((id.val) as usize, 1.0)
effectiveness.resize((id.0) as usize, 1.0)
}
id
}
@ -154,9 +141,9 @@ impl TypeLibrary for TypeLibraryImpl {
*self
.effectiveness
.write()
.get_mut((attacking.val - 1) as usize)
.get_mut((attacking.0 - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: attacking })?
.get_mut((defending.val - 1) as usize)
.get_mut((defending.0 - 1) as usize)
.ok_or(PkmnError::InvalidTypeIdentifier { type_id: defending })? = effectiveness;
Ok(())
}
@ -169,7 +156,6 @@ pub mod tests {
use assert_approx_eq::assert_approx_eq;
use super::*;
use crate::static_data::libraries::type_library::TypeLibrary;
pub fn build() -> TypeLibraryImpl {
let mut lib = TypeLibraryImpl::new(2);

View File

@ -1,40 +1,24 @@
use crate::StringKey;
#[doc(inline)]
pub use growth_rates::*;
#[doc(inline)]
pub use items::*;
#[doc(inline)]
pub use libraries::*;
#[doc(inline)]
pub use moves::*;
#[doc(inline)]
pub use natures::*;
#[doc(inline)]
pub use species_data::*;
#[doc(inline)]
pub use statistic_set::*;
#[doc(inline)]
pub use statistics::*;
#[doc(inline)]
pub use time_of_day::*;
#[doc(inline)] pub use growth_rates::*;
#[doc(inline)] pub use items::*;
#[doc(inline)] pub use libraries::*;
#[doc(inline)] pub use moves::*;
#[doc(inline)] pub use natures::*;
#[doc(inline)] pub use species_data::*;
#[doc(inline)] pub use statistic_set::*;
#[doc(inline)] pub use statistics::*;
#[doc(inline)] pub use time_of_day::*;
use std::fmt::{Display, Formatter};
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[doc(inline)]
pub use growth_rates::tests::*;
#[doc(inline)]
pub use items::tests::*;
#[doc(inline)]
pub use libraries::tests::*;
#[doc(inline)]
pub use moves::tests::*;
#[doc(inline)]
pub use natures::tests::*;
#[doc(inline)]
pub use species_data::tests::*;
#[doc(inline)] pub use growth_rates::tests::*;
#[doc(inline)] pub use items::tests::*;
#[doc(inline)] pub use moves::tests::*;
#[doc(inline)] pub use natures::tests::*;
#[doc(inline)] pub use species_data::tests::*;
}
/// Growth rates define how fast a Pokemon can level up.
@ -71,27 +55,19 @@ pub enum Parameter {
}
impl From<bool> for Parameter {
fn from(b: bool) -> Self {
Parameter::Bool(b)
}
fn from(b: bool) -> Self { Parameter::Bool(b) }
}
impl From<i64> for Parameter {
fn from(i: i64) -> Self {
Parameter::Int(i)
}
fn from(i: i64) -> Self { Parameter::Int(i) }
}
impl From<f32> for Parameter {
fn from(f: f32) -> Self {
Parameter::Float(f)
}
fn from(f: f32) -> Self { Parameter::Float(f) }
}
impl From<StringKey> for Parameter {
fn from(s: StringKey) -> Self {
Parameter::String(s)
}
fn from(s: StringKey) -> Self { Parameter::String(s) }
}
impl Display for Parameter {

View File

@ -1,16 +1,11 @@
#[doc(inline)]
pub use move_data::*;
#[doc(inline)]
pub use secondary_effect::*;
#[doc(inline)] pub use move_data::*;
#[doc(inline)] pub use secondary_effect::*;
#[cfg(test)]
pub(crate) mod tests {
use super::*;
#[doc(inline)]
pub use move_data::tests::*;
#[doc(inline)]
pub use secondary_effect::tests::*;
#[doc(inline)] pub use move_data::tests::*;
}
/// The data belonging to a certain move.

View File

@ -1,6 +1,5 @@
use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")] use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::sync::Arc;
@ -11,6 +10,7 @@ use crate::StringKey;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum MoveCategory {
/// A physical move uses the physical attack stats and physical defense stats to calculate damage.
@ -24,6 +24,7 @@ pub enum MoveCategory {
/// The move target defines what kind of targets the move can touch.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum MoveTarget {
/// Adjacent allows a move to target any Pokemon that is either directly to the left or right of
@ -149,54 +150,32 @@ impl MoveDataImpl {
impl MoveData for MoveDataImpl {
/// The name of the move.
fn name(&self) -> &StringKey {
&self.name
}
fn name(&self) -> &StringKey { &self.name }
/// The attacking type of the move.
fn move_type(&self) -> TypeIdentifier {
self.move_type
}
fn move_type(&self) -> TypeIdentifier { self.move_type }
/// The category of the move.
fn category(&self) -> MoveCategory {
self.category
}
fn category(&self) -> MoveCategory { self.category }
/// The base power, not considering any modifiers, the move has.
fn base_power(&self) -> u8 {
self.base_power
}
fn base_power(&self) -> u8 { self.base_power }
/// The accuracy of the move in percentage. Should be 255 for moves that always hit.
fn accuracy(&self) -> u8 {
self.accuracy
}
fn accuracy(&self) -> u8 { self.accuracy }
/// The number of times the move can be used. This can be modified on actually learned moves using
/// PP-Ups
fn base_usages(&self) -> u8 {
self.base_usages
}
fn base_usages(&self) -> u8 { self.base_usages }
/// How the move handles targets.
fn target(&self) -> MoveTarget {
self.target
}
fn target(&self) -> MoveTarget { self.target }
/// The priority of the move. A higher priority means the move should go before other moves.
fn priority(&self) -> i8 {
self.priority
}
fn priority(&self) -> i8 { self.priority }
/// The optional secondary effect the move has.
fn secondary_effect(&self) -> &Option<Arc<dyn SecondaryEffect>> {
&self.secondary_effect
}
fn secondary_effect(&self) -> &Option<Arc<dyn SecondaryEffect>> { &self.secondary_effect }
/// Checks if the move has a specific flag.
fn has_flag(&self, key: &StringKey) -> bool {
self.flags.contains::<StringKey>(key)
}
fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains::<StringKey>(key) }
/// Checks if the move has a specific flag.
fn has_flag_by_hash(&self, key_hash: u32) -> bool {
self.flags.contains::<u32>(&key_hash)
}
fn has_flag_by_hash(&self, key_hash: u32) -> bool { self.flags.contains::<u32>(&key_hash) }
}
#[cfg(test)]

View File

@ -1,5 +1,6 @@
use crate::static_data::Parameter;
use crate::StringKey;
use hashbrown::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
@ -10,7 +11,7 @@ pub trait SecondaryEffect: Debug {
/// The name of the effect.
fn effect_name(&self) -> &StringKey;
/// A list of parameters for the effect.
fn parameters(&self) -> &Vec<Arc<Parameter>>;
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>>;
}
/// 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.
effect_name: StringKey,
/// A list of parameters for the effect.
parameters: Vec<Arc<Parameter>>,
parameters: HashMap<StringKey, Arc<Parameter>>,
}
impl SecondaryEffectImpl {
/// 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 {
chance,
effect_name,
@ -37,17 +38,11 @@ impl SecondaryEffectImpl {
impl SecondaryEffect for SecondaryEffectImpl {
/// The chance in percentages that the effect triggers. -1 to make it always trigger.
fn chance(&self) -> f32 {
self.chance
}
fn chance(&self) -> f32 { self.chance }
/// The name of the effect.
fn effect_name(&self) -> &StringKey {
&self.effect_name
}
fn effect_name(&self) -> &StringKey { &self.effect_name }
/// A list of parameters for the effect.
fn parameters(&self) -> &Vec<Arc<Parameter>> {
&self.parameters
}
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>> { &self.parameters }
}
#[cfg(test)]
@ -57,26 +52,23 @@ pub(crate) mod tests {
use super::*;
use assert_approx_eq::assert_approx_eq;
use crate::static_data::moves::secondary_effect::SecondaryEffect;
use crate::static_data::SecondaryEffectImpl;
mockall::mock! {
#[derive(Debug)]
pub SecondaryEffect{}
impl SecondaryEffect for SecondaryEffect {
fn chance(&self) -> f32;
fn effect_name(&self) -> &StringKey;
fn parameters(&self) -> &Vec<Arc<Parameter >>;
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter >>;
}
}
#[test]
fn create_secondary_effect() {
let empty = SecondaryEffectImpl::new(0.0, "".into(), vec![]);
let empty = SecondaryEffectImpl::new(0.0, "".into(), HashMap::new());
assert_approx_eq!(empty.chance(), 0.0);
assert_eq!(empty.effect_name(), &"".into());
assert_eq!(empty.parameters().len(), 0);
let set = SecondaryEffectImpl::new(50.0, "foo".into(), Vec::new());
let set = SecondaryEffectImpl::new(50.0, "foo".into(), HashMap::new());
assert_approx_eq!(set.chance(), 50.0);
assert_eq!(set.effect_name(), &"foo".into());
assert_eq!(set.parameters().len(), 0);

View File

@ -1,5 +1,6 @@
use crate::static_data::Parameter;
use crate::StringKey;
use hashbrown::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
@ -10,7 +11,7 @@ pub trait Ability: Debug {
/// The name of the script effect of the ability.
fn effect(&self) -> &StringKey;
/// The parameters for the script effect of the ability.
fn parameters(&self) -> &Vec<Arc<Parameter>>;
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>>;
}
/// 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.
effect: StringKey,
/// The parameters for the script effect of the ability.
parameters: Vec<Arc<Parameter>>,
parameters: HashMap<StringKey, Arc<Parameter>>,
}
impl AbilityImpl {
/// 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 {
name: name.clone(),
effect: effect.clone(),
@ -37,17 +38,11 @@ impl AbilityImpl {
impl Ability for AbilityImpl {
/// The name of the ability.
fn name(&self) -> &StringKey {
&self.name
}
fn name(&self) -> &StringKey { &self.name }
/// The name of the script effect of the ability.
fn effect(&self) -> &StringKey {
&self.effect
}
fn effect(&self) -> &StringKey { &self.effect }
/// The parameters for the script effect of the ability.
fn parameters(&self) -> &Vec<Arc<Parameter>> {
&self.parameters
}
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter>> { &self.parameters }
}
/// An ability index allows us to find an ability on a form. It combines a bool for whether the
@ -74,7 +69,7 @@ pub(crate) mod tests {
impl Ability for Ability {
fn name(&self) -> &StringKey;
fn effect(&self) -> &StringKey;
fn parameters(&self) -> &Vec<Arc<Parameter >>;
fn parameters(&self) -> &HashMap<StringKey, Arc<Parameter >>;
}
}
}

View File

@ -44,12 +44,12 @@ pub trait Form: Debug {
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
/// Gets an ability from the form.
fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey>;
fn get_ability(&self, index: AbilityIndex) -> Result<StringKey>;
/// Gets a random ability from the form.
fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey>;
fn get_random_ability(&self, rand: &mut Random) -> Result<StringKey>;
/// Gets a random hidden ability from the form.
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey>;
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<StringKey>;
/// Check if the form has a specific flag set.
fn has_flag(&self, key: &StringKey) -> bool;
@ -118,56 +118,32 @@ impl FormImpl {
impl Form for FormImpl {
/// The name of the form.
fn name(&self) -> &StringKey {
&self.name
}
fn name(&self) -> &StringKey { &self.name }
/// The height of the form in meters.
fn height(&self) -> f32 {
self.height
}
fn height(&self) -> f32 { self.height }
/// The weight of the form in kilograms.
fn weight(&self) -> f32 {
self.weight
}
fn weight(&self) -> f32 { self.weight }
/// The base amount of experience that is gained when beating a Pokemon with this form.
fn base_experience(&self) -> u32 {
self.base_experience
}
fn base_experience(&self) -> u32 { self.base_experience }
/// The normal types a Pokemon with this form has.
fn types(&self) -> &Vec<TypeIdentifier> {
&self.types
}
fn types(&self) -> &Vec<TypeIdentifier> { &self.types }
/// The inherent values of a form of species that are used for the stats of a Pokemon.
fn base_stats(&self) -> &Arc<StaticStatisticSet<u16>> {
&self.base_stats
}
fn base_stats(&self) -> &Arc<StaticStatisticSet<u16>> { &self.base_stats }
/// The possible abilities a Pokemon with this form can have.
fn abilities(&self) -> &Vec<StringKey> {
&self.abilities
}
fn abilities(&self) -> &Vec<StringKey> { &self.abilities }
/// The possible hidden abilities a Pokemon with this form can have.
fn hidden_abilities(&self) -> &Vec<StringKey> {
&self.hidden_abilities
}
fn hidden_abilities(&self) -> &Vec<StringKey> { &self.hidden_abilities }
/// The moves a Pokemon with this form can learn.
fn moves(&self) -> &Arc<dyn LearnableMoves> {
&self.moves
}
fn moves(&self) -> &Arc<dyn LearnableMoves> { &self.moves }
/// Arbitrary flags can be set on a form for scripting use.
fn flags(&self) -> &HashSet<StringKey> {
&self.flags
}
fn flags(&self) -> &HashSet<StringKey> { &self.flags }
/// Get a type of the move at a certain index.
fn get_type(&self, index: usize) -> Result<TypeIdentifier> {
Ok(*self.types.get_res(index)?)
}
fn get_type(&self, index: usize) -> Result<TypeIdentifier> { Ok(*self.types.get_res(index)?) }
/// Gets a single base stat value.
fn get_base_stat(&self, stat: Statistic) -> u16 {
self.base_stats.get_stat(stat)
}
fn get_base_stat(&self, stat: Statistic) -> u16 { self.base_stats.get_stat(stat) }
/// Find the index of an ability that can be on this form.
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex> {
@ -191,39 +167,35 @@ impl Form for FormImpl {
}
/// Gets an ability from the form.
fn get_ability(&self, index: AbilityIndex) -> Result<&StringKey> {
fn get_ability(&self, index: AbilityIndex) -> Result<StringKey> {
if index.hidden {
Ok(self.hidden_abilities.get_res(index.index as usize)?)
self.hidden_abilities.get_res(index.index as usize).map(|s| s.clone())
} else {
Ok(self.abilities.get_res(index.index as usize)?)
self.abilities.get_res(index.index as usize).map(|s| s.clone())
}
}
/// Gets a random ability from the form.
fn get_random_ability(&self, rand: &mut Random) -> Result<&StringKey> {
fn get_random_ability(&self, rand: &mut Random) -> Result<StringKey> {
ensure!(!self.abilities.is_empty(), "No abilities on form");
self.abilities
.get_res(rand.get_between_unsigned(0, self.abilities.len() as u32) as usize)
.map(|s| s.clone())
}
/// Gets a random hidden ability from the form.
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<&StringKey> {
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<StringKey> {
ensure!(!self.hidden_abilities.is_empty(), "No hidden abilities on form");
self.hidden_abilities
.get_res(rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize)
.map(|s| s.clone())
}
/// Check if the form has a specific flag set.
fn has_flag(&self, key: &StringKey) -> bool {
self.flags.contains(key)
}
fn has_flag(&self, key: &StringKey) -> bool { self.flags.contains(key) }
fn has_flag_by_hash(&self, key_hash: u32) -> bool {
self.flags.contains::<u32>(&key_hash)
}
fn has_flag_by_hash(&self, key_hash: u32) -> bool { self.flags.contains::<u32>(&key_hash) }
fn eq(&self, other: &dyn Form) -> bool {
std::ptr::eq(self, other as *const dyn Form as *const Self)
}
fn eq(&self, other: &dyn Form) -> bool { std::ptr::eq(self, other as *const dyn Form as *const Self) }
}
#[cfg(test)]
@ -249,9 +221,9 @@ pub(crate) mod tests {
fn get_type(&self, index: usize) -> Result<TypeIdentifier>;
fn get_base_stat(&self, stat: Statistic) -> u16;
fn find_ability_index(&self, ability: &dyn Ability) -> Option<AbilityIndex>;
fn get_ability<'a>(&'a self, index: AbilityIndex) -> Result<&'a StringKey>;
fn get_random_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
fn get_random_hidden_ability<'a>(&'a self, rand: &mut Random) -> Result<&'a StringKey>;
fn get_ability(&self, index: AbilityIndex) -> Result<StringKey>;
fn get_random_ability(&self, rand: &mut Random) -> Result<StringKey>;
fn get_random_hidden_ability(&self, rand: &mut Random) -> Result<StringKey>;
fn has_flag(&self, key: &StringKey) -> bool;
fn has_flag_by_hash(&self, key_hash: u32) -> bool;
fn eq(&self, other: &dyn Form) -> bool;

View File

@ -4,6 +4,7 @@
/// that allows for a more progressive gender system for those that want it?
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum Gender {
/// The Pokemon has no gender.

View File

@ -1,22 +1,15 @@
#[doc(inline)]
pub use ability::*;
#[doc(inline)]
pub use evolution_data::*;
#[doc(inline)]
pub use form::*;
#[doc(inline)]
pub use gender::*;
#[doc(inline)]
pub use learnable_moves::*;
#[doc(inline)]
pub use species::*;
#[doc(inline)] pub use ability::*;
#[doc(inline)] pub use evolution_data::*;
#[doc(inline)] pub use form::*;
#[doc(inline)] pub use gender::*;
#[doc(inline)] pub use learnable_moves::*;
#[doc(inline)] pub use species::*;
#[cfg(test)]
pub(crate) mod tests {
pub use super::ability::tests::*;
pub use super::form::tests::*;
pub use super::learnable_moves::tests::*;
pub use super::species::tests::*;
pub use super::ability::tests::*;
}
/// An ability is a passive effect in battle that is attached to a Pokemon.

View File

@ -54,29 +54,17 @@ where
}
/// The health point stat value.
pub fn hp(&self) -> T {
self.hp.load(Ordering::Relaxed)
}
pub fn hp(&self) -> T { self.hp.load(Ordering::Relaxed) }
/// The physical attack stat value.
pub fn attack(&self) -> T {
self.attack.load(Ordering::Relaxed)
}
pub fn attack(&self) -> T { self.attack.load(Ordering::Relaxed) }
/// The physical defense stat value.
pub fn defense(&self) -> T {
self.defense.load(Ordering::Relaxed)
}
pub fn defense(&self) -> T { self.defense.load(Ordering::Relaxed) }
/// The special attack stat value.
pub fn special_attack(&self) -> T {
self.special_attack.load(Ordering::Relaxed)
}
pub fn special_attack(&self) -> T { self.special_attack.load(Ordering::Relaxed) }
/// The special defense stat value.
pub fn special_defense(&self) -> T {
self.special_defense.load(Ordering::Relaxed)
}
pub fn special_defense(&self) -> T { self.special_defense.load(Ordering::Relaxed) }
/// The speed stat value.
pub fn speed(&self) -> T {
self.speed.load(Ordering::Relaxed)
}
pub fn speed(&self) -> T { self.speed.load(Ordering::Relaxed) }
/// Get the value of a specific stat
pub fn get_stat(&self, stat: Statistic) -> T {
@ -165,29 +153,17 @@ where
}
/// The health point stat value.
pub const fn hp(&self) -> T {
self.hp
}
pub const fn hp(&self) -> T { self.hp }
/// The physical attack stat value.
pub const fn attack(&self) -> T {
self.attack
}
pub const fn attack(&self) -> T { self.attack }
/// The physical defense stat value.
pub const fn defense(&self) -> T {
self.defense
}
pub const fn defense(&self) -> T { self.defense }
/// The special attack stat value.
pub const fn special_attack(&self) -> T {
self.special_attack
}
pub const fn special_attack(&self) -> T { self.special_attack }
/// The special defense stat value.
pub const fn special_defense(&self) -> T {
self.special_defense
}
pub const fn special_defense(&self) -> T { self.special_defense }
/// The speed stat value.
pub const fn speed(&self) -> T {
self.speed
}
pub const fn speed(&self) -> T { self.speed }
/// Get the value of a specific stat
pub const fn get_stat(&self, stat: Statistic) -> T {
@ -245,14 +221,10 @@ where
{
/// The lowest value a value on the set can have.
#[allow(clippy::unwrap_used)] // Should never fail
pub fn min() -> T {
<T as NumCast>::from(MIN).unwrap()
}
pub fn min() -> T { <T as NumCast>::from(MIN).unwrap() }
/// The highest value a value on the set can have.
#[allow(clippy::unwrap_used)] // Should never fail
pub fn max() -> T {
<T as NumCast>::from(MAX).unwrap()
}
pub fn max() -> T { <T as NumCast>::from(MAX).unwrap() }
/// Takes the underlying primary value, clamp it between the two values, and give it back as
/// atomic.
@ -274,29 +246,17 @@ where
}
/// The health point stat value.
pub fn hp(&self) -> T {
self.hp.load(Ordering::Relaxed)
}
pub fn hp(&self) -> T { self.hp.load(Ordering::Relaxed) }
/// The physical attack stat value.
pub fn attack(&self) -> T {
self.attack.load(Ordering::Relaxed)
}
pub fn attack(&self) -> T { self.attack.load(Ordering::Relaxed) }
/// The physical defense stat value.
pub fn defense(&self) -> T {
self.defense.load(Ordering::Relaxed)
}
pub fn defense(&self) -> T { self.defense.load(Ordering::Relaxed) }
/// The special attack stat value.
pub fn special_attack(&self) -> T {
self.special_attack.load(Ordering::Relaxed)
}
pub fn special_attack(&self) -> T { self.special_attack.load(Ordering::Relaxed) }
/// The special defense stat value.
pub fn special_defense(&self) -> T {
self.special_defense.load(Ordering::Relaxed)
}
pub fn special_defense(&self) -> T { self.special_defense.load(Ordering::Relaxed) }
/// The speed stat value.
pub fn speed(&self) -> T {
self.speed.load(Ordering::Relaxed)
}
pub fn speed(&self) -> T { self.speed.load(Ordering::Relaxed) }
/// Gets a specific stat.
pub fn get_stat(&self, stat: Statistic) -> T {
@ -399,6 +359,28 @@ where
}
}
impl<T, const MIN: i64, const MAX: i64> From<&ClampedStatisticSet<T, MIN, MAX>> for StatisticSet<T>
where
T: PrimitiveAtom,
T: Atom,
T: PrimitiveAtomInteger,
<T as Atom>::Repr: PrimitiveAtomInteger,
T: AtomInteger,
T: NumCast,
T: PrimInt,
{
fn from(value: &ClampedStatisticSet<T, MIN, MAX>) -> Self {
Self {
hp: Atomic::<T>::new(value.hp()),
attack: Atomic::<T>::new(value.attack()),
defense: Atomic::<T>::new(value.defense()),
special_attack: Atomic::<T>::new(value.special_attack()),
special_defense: Atomic::<T>::new(value.special_defense()),
speed: Atomic::<T>::new(value.speed()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,21 +1,27 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")] use serde::{Deserialize, Serialize};
/// Stats are numerical values on Pokemon that are used in battle.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum Statistic {
/// Health Points determine how much damage a Pokemon can receive before fainting.
#[cfg_attr(feature = "rune", rune(constructor))]
HP,
/// Attack determines how much damage a Pokemon deals when using a physical attack.
#[cfg_attr(feature = "rune", rune(constructor))]
Attack,
/// Defense determines how much damage a Pokemon receives when it is hit by a physical attack.
#[cfg_attr(feature = "rune", rune(constructor))]
Defense,
/// Special Attack determines how much damage a Pokemon deals when using a special attack.
#[cfg_attr(feature = "rune", rune(constructor))]
SpecialAttack,
/// Special Defense determines how much damage a Pokemon receives when it is hit by a special attack.
#[cfg_attr(feature = "rune", rune(constructor))]
SpecialDefense,
/// Speed determines the order that a Pokemon can act in battle.
#[cfg_attr(feature = "rune", rune(constructor))]
Speed,
}

View File

@ -1,6 +1,7 @@
/// The time of day. These values are the 4 different groups of time of day in Pokemon games since
/// gen 5. The exact times these correspond to differ between games.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "rune", derive(rune::Any))]
#[repr(u8)]
pub enum TimeOfDay {
/// The morning.

View File

@ -5,7 +5,7 @@ use std::fs::File;
use std::io::{BufReader, Read};
use std::sync::Arc;
use hashbrown::HashSet;
use hashbrown::{HashMap, HashSet};
use num_traits::PrimInt;
use project_root::get_project_root;
use serde_json::Value;
@ -223,10 +223,11 @@ pub fn load_abilities(path: &String) -> Arc<dyn AbilityLibrary> {
if let Some(e) = value.get("effect") {
effect = e.as_str().unwrap().into();
}
let mut parameters = Vec::new();
if let Some(p) = value.get("parameters") {
for par in p.as_array().unwrap() {
parameters.push(parse_parameter(par));
let mut parameters = HashMap::new();
if let Some(pars) = value.get("parameters") {
let pars = pars.as_object().unwrap();
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") {
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") {
let pars = pars.as_array().unwrap();
let pars = pars.as_object().unwrap();
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)
}
#[cfg(not(feature = "wasm"))]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
Arc::new(EmptyScriptResolver::default())
}
#[cfg(not(any(feature = "wasm", feature = "rune")))]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> { Arc::new(EmptyScriptResolver::default()) }
#[cfg(feature = "wasm")]
fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
@ -468,6 +467,28 @@ fn load_script_resolver(path: &String) -> Arc<dyn ScriptResolver> {
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(
name: StringKey,
value: &Value,

View File

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

View File

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

View File

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

View File

@ -10,12 +10,10 @@ fn integration_tests(input: &Path) {
let mut str: String = "".to_string();
let mut file = File::open(input).unwrap();
file.read_to_string(&mut str).unwrap();
let test_case = serde_yaml::from_str::<TestCase>(&str).unwrap();
let test_case = serde_yml::from_str::<TestCase>(&str).unwrap();
println!(" Running integration test {}", test_case.name);
test_case.run_test(get_library());
}
#[test]
fn basic_single_turn() {
integration_tests(Path::new("tests/test_cases/basic_single_turn.yaml"));
}
fn basic_single_turn() { integration_tests(Path::new("tests/test_cases/basic_single_turn.yaml")); }

View File

@ -5,10 +5,7 @@
use std::sync::{Arc, LazyLock};
use pkmn_lib::dynamic_data::{
Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PokemonBuilder, PokemonParty,
ScriptCategory, ScriptContainer, ScriptOwnerData, TurnChoice, VolatileScriptsOwner,
};
use pkmn_lib::dynamic_data::{DynamicLibrary, PassChoice, PokemonBuilder, ScriptCategory, ScriptOwnerData, TurnChoice};
use crate::common::library_loader;
@ -17,9 +14,7 @@ pub mod datatests;
static LIBRARY: LazyLock<Arc<dyn DynamicLibrary>> = LazyLock::new(|| library_loader::load_library().library);
fn get_library() -> Arc<dyn DynamicLibrary> {
LIBRARY.clone()
}
fn get_library() -> Arc<dyn DynamicLibrary> { LIBRARY.clone() }
#[test]
fn validate_library_load() {
@ -35,7 +30,7 @@ fn validate_library_load() {
\n\t- Abilities load time: {} ms\
\n\t- Moves 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(),
result.types_load_time.num_milliseconds(),
@ -49,6 +44,32 @@ fn validate_library_load() {
);
}
#[test]
fn rune_test() {
let result = library_loader::load_library();
let library = result.library;
let p1 = PokemonBuilder::new(library.clone(), "charizard".into(), 100)
.build()
.unwrap();
let script = library
.load_script(
ScriptOwnerData::Pokemon(p1.weak()),
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]
fn load_non_existing_wasm_script() {
let start_time = chrono::Utc::now();
@ -63,7 +84,7 @@ fn load_non_existing_wasm_script() {
\n\t- Abilities load time: {} ms\
\n\t- Moves 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(),
result.types_load_time.num_milliseconds(),
@ -82,75 +103,3 @@ fn load_non_existing_wasm_script() {
assert!(script.is_none());
}
/// Assurance has the interesting properties that it creates a special data script internally, and
/// deletes that data script through the get_owner functionality.
#[test]
fn validate_assurance() {
let lib = get_library();
let p1 = PokemonBuilder::new(lib.clone(), "charizard".into(), 100)
.learn_move("assurance".into())
.build()
.unwrap();
let p2 = PokemonBuilder::new(lib.clone(), "venusaur".into(), 100)
.build()
.unwrap();
let party1 = Arc::new(
BattleParty::new(
Arc::new(PokemonParty::new_from_vec(vec![Some(p1.clone())])),
vec![(0, 0)],
)
.unwrap(),
);
let party2 = Arc::new(
BattleParty::new(
Arc::new(PokemonParty::new_from_vec(vec![Some(p2.clone())])),
vec![(1, 0)],
)
.unwrap(),
);
let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None);
battle.sides()[0].set_pokemon(0, Some(p1.clone())).unwrap();
battle.sides()[1].set_pokemon(0, Some(p2.clone())).unwrap();
let script = lib
.load_script(
ScriptOwnerData::None,
ScriptCategory::Move,
&"double_power_if_target_damaged_in_turn".into(),
)
.unwrap()
.unwrap();
let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone();
let choice = Arc::new(TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0)));
script.on_before_turn(&choice).unwrap();
assert!(battle.sides()[1].has_volatile_script(&"double_power_if_target_damaged_in_turn_data".into()));
let executing_move = Arc::new(ExecutingMove::new(
vec![],
1,
p1,
mv.clone(),
mv.move_data().clone(),
ScriptContainer::default(),
));
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 20_u8);
let s = battle.sides()[1].get_volatile_script(&"double_power_if_target_damaged_in_turn_data".into());
let binding = s.as_ref().unwrap().get().unwrap().read();
let data_script = binding.as_ref().unwrap();
data_script.on_damage(&p2, DamageSource::Misc, 100, 50).unwrap();
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 40_u8);
data_script.on_end_turn().unwrap();
assert!(!battle.sides()[1].has_volatile_script(&"double_power_if_target_damaged_in_turn_data".into()));
}