Initial work on adding documentation, reorganises modules
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2022-06-19 21:34:08 +02:00
parent 715f16e2b8
commit 314e9dbe1a
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
49 changed files with 806 additions and 473 deletions

View File

@ -1,2 +1,8 @@
/// The type we store a level in. As our implementation is aimed at normal Pokemon behaviour, a u8
/// is probably enough, as we'd go up to 100. For other users this might however not be enough. This
/// allows them to easily change it.
pub type LevelInt = u8;
/// The amount of moves a Pokemon can have at once. This is set to 4, as that is the default in most
/// current Pokemon generations. A developer should be able to modify this however.
pub const MAX_MOVES: usize = 4;

View File

@ -1,30 +1,49 @@
use crate::dynamic_data::models::learned_move::LearnedMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::script::ScriptContainer;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use parking_lot::RwLock;
use std::cmp::Ordering;
use std::sync::Arc;
use parking_lot::RwLock;
use crate::dynamic_data::LearnedMove;
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::{ScriptSource, ScriptSourceData, ScriptWrapper};
/// The data on a turn choice that should be contained in every turn choice, regardless of type.
#[derive(Debug)]
struct CommonChoiceData<'user, 'library> {
/// The user of the turn choice
user: Arc<Pokemon<'user, 'library>>,
/// The speed of the user at the beginning of the turn.
speed: u32,
/// This random value is set at the beginning of the turn. It is used for tie breaking of the
/// turn order in a predictable way, regardless of implementation and hardware.
random_value: u32,
/// Whether or not the choice has failed. A failed choice will stop running, and execute special
/// fail handling during turn execution.
has_failed: bool,
/// The data we can use to retrieve scripts that are affecting this choice. This will be written
/// to once: when we need to initialize it. After that, this is only used to read. To prevent a
/// read while we're writing to it, this is a RwLock.
script_source_data: RwLock<ScriptSourceData>,
}
/// This enum defines a single choice for a Pokemon for a battle turn.
#[derive(Debug)]
pub enum TurnChoice<'user, 'library> {
/// A move choice tells a Pokemon to use a move on a target for this turn.
Move(MoveChoice<'user, 'library>),
/// An item choice tells a Pokemon to use an item.
Item(ItemChoice<'user, 'library>),
/// A switch choice tells a Pokemon to switch with another Pokemon from the party.
Switch(SwitchChoice<'user, 'library>),
/// A flee choice tells a Pokemon to flee from battle.
Flee(FleeChoice<'user, 'library>),
/// A pass choice tells the user to do nothing that turn.
Pass(PassChoice<'user, 'library>),
}
impl<'user, 'library> TurnChoice<'user, 'library> {
/// The shared choice data between each of the different turn choices.
fn choice_data(&self) -> &CommonChoiceData<'user, 'library> {
match self {
TurnChoice::Move(data) => &data.choice_data,
@ -34,6 +53,7 @@ impl<'user, 'library> TurnChoice<'user, 'library> {
TurnChoice::Pass(data) => &data.choice_data,
}
}
/// The shared choice data between each of the different turn choices.
fn choice_data_mut(&mut self) -> &mut Box<CommonChoiceData<'user, 'library>> {
match self {
TurnChoice::Move(data) => &mut data.choice_data,
@ -44,34 +64,49 @@ impl<'user, 'library> TurnChoice<'user, 'library> {
}
}
/// Get the user of the given choice.
pub fn user(&self) -> &Arc<Pokemon<'user, 'library>> {
&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
}
/// Get the mutable 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_mut(&mut self) -> &mut u32 {
&mut self.choice_data_mut().speed
}
/// 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
}
/// 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(&mut self) {
self.choice_data_mut().has_failed = true
}
/// 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
}
/// This sets the above random value.
pub(crate) fn set_random_value(&mut self, val: u32) {
self.choice_data_mut().random_value = val;
}
/// Helper function to get the move choice data from a turn. Note that this will panic if not
/// used on a move choice.
pub(crate) fn get_move_turn_data<'b>(&'b self) -> &'b MoveChoice<'user, 'library> {
if let TurnChoice::Move(data) = self {
return data;
@ -122,17 +157,25 @@ impl<'user, 'library> ScriptSource<'user> for TurnChoice<'user, 'library> {
}
}
/// The data attached to a move choice.
#[derive(Debug)]
pub struct MoveChoice<'user, 'library> {
/// The move that is used for this choice.
used_move: Arc<LearnedMove<'library>>,
/// The side this move is aimed at.
target_side: u8,
/// The index of the Pokemon on the side we're aiming at.
target_index: u8,
/// The move script.
script: ScriptContainer,
/// The priority of the move choice at the beginning of the turn.
priority: i8,
/// The common turn choice data.
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> MoveChoice<'user, 'library> {
/// Initializes the data for a new move choice.
pub fn new(
user: Arc<Pokemon<'user, 'library>>,
used_move: Arc<LearnedMove<'library>>,
@ -155,29 +198,35 @@ impl<'user, 'library> MoveChoice<'user, 'library> {
}
}
/// The actual learned move on the Pokemon we use for this choice.
pub fn used_move(&self) -> &Arc<LearnedMove<'library>> {
&self.used_move
}
/// The target side the move is aimed at.
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
}
/// The priority of the move choice at the beginning of the turn.
pub fn priority(&self) -> i8 {
self.priority
}
/// The priority of the move choice at the beginning of the turn.
pub fn priority_mut(&mut self) -> &mut i8 {
&mut self.priority
}
/// The user of the choice.
pub fn user(&self) -> &Arc<Pokemon<'user, 'library>> {
&self.choice_data.user
}
/// The move script of the choice.
pub fn script(&self) -> &ScriptContainer {
&self.script
}
pub fn priority_mut(&mut self) -> &mut i8 {
&mut self.priority
}
}
impl<'user, 'library> ScriptSource<'user> for MoveChoice<'user, 'library> {
@ -199,12 +248,15 @@ impl<'user, 'library> ScriptSource<'user> for MoveChoice<'user, 'library> {
}
}
/// The data given when we select an item choice.
#[derive(Debug)]
pub struct ItemChoice<'user, 'library> {
/// The shared data of all turn choices.
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> ItemChoice<'user, 'library> {
/// Initialised a new item choice.
pub fn new(user: Arc<Pokemon<'user, 'library>>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
@ -234,12 +286,15 @@ impl<'user, 'library> ScriptSource<'user> for ItemChoice<'user, 'library> {
}
}
/// The data given when we select a switch choice.
#[derive(Debug)]
pub struct SwitchChoice<'user, 'library> {
/// The shared data of all turn choices.
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> SwitchChoice<'user, 'library> {
/// Initialise the turn choice data.
pub fn new(user: Arc<Pokemon<'user, 'library>>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
@ -269,12 +324,15 @@ impl<'user, 'library> ScriptSource<'user> for SwitchChoice<'user, 'library> {
}
}
/// The data given when we select a flee choice.
#[derive(Debug)]
pub struct FleeChoice<'user, 'library> {
/// The common data all turn choices share.
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> FleeChoice<'user, 'library> {
/// Initialises a new flee choice.
pub fn new(user: Arc<Pokemon<'user, 'library>>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {
@ -304,12 +362,15 @@ impl<'user, 'library> ScriptSource<'user> for FleeChoice<'user, 'library> {
}
}
/// The data given when we select a pass choice.
#[derive(Debug)]
pub struct PassChoice<'user, 'library> {
/// The common data of all turn choices.
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> PassChoice<'user, 'library> {
/// Initialised a new pass choice.
pub fn new(user: Arc<Pokemon<'user, 'library>>) -> Self {
Self {
choice_data: Box::new(CommonChoiceData {

View File

@ -0,0 +1,111 @@
use std::fmt::{Debug, Formatter};
use crate::dynamic_data::DamageSource;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
use crate::static_data::Form;
use crate::static_data::Species;
/// The event hook is used to store external functions that listen to events.
///
/// Events happen in many
/// different places in the battle, and can be used for GUI applications to show that these events
/// are happening.
#[derive(Default)]
pub struct EventHook {
/// All the registered event listeners on the hook.
evt_hook_function: Vec<fn(&Box<&Event>)>,
}
impl<'battle, 'library> EventHook {
/// Register a new listener. This will start receiving all events in the battle. Multiple event
/// listeners can exist at the same time. Note that for these functions the event will be disposed
/// of after the event is finished being sent.
pub fn register_listener(&mut self, func: fn(&Box<&Event>)) {
self.evt_hook_function.push(func);
}
/// Run a new event. This will send the event to all externally defined event listeners. It will
/// dispose of the event afterwards.
pub fn trigger<'b>(&self, evt: Event<'b, 'battle, 'library>) {
let b = Box::new(&evt);
for f in &self.evt_hook_function {
f(&b);
}
}
}
impl Debug for EventHook {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}
/// The different events that can occur during the battle, for which a GUI should show something.
#[derive(Debug)]
pub enum Event<'own, 'battle, 'library> {
/// A switch event happens when a Pokemon gets switched out for something else.
Switch {
/// The side the Pokemon got switched from/on
side_index: u8,
/// The index of the Pokemon that got switched in/out on its side
index: u8,
/// The new Pokemon that will be on the spot. If none, the spot will now be empty.
pokemon: Option<&'own Pokemon<'battle, 'library>>,
},
/// A swap event happens when two Pokemon on a side swap positions. Note that this is rare.
Swap {
/// The side the Pokemon swapped on.
side_index: u8,
/// The index on the side of the first Pokemon that was swapped.
index_a: u8,
/// The index on the side of the second pokemon that was swapped.
index_b: u8,
},
/// This event happens when a Pokemon changes species during battle. While not normally occuring
/// this can be useful for things such as mid-battle evolutions, which some fanmade implementations
/// enjoy.
SpeciesChange {
/// The pokemon that changed species.
pokemon: &'own Pokemon<'battle, 'library>,
/// The new species of the Pokemon.
species: &'own Species,
/// The form of the species the Pokemon will have.
form: &'own Form,
},
/// This event happens when a Pokemon changes form during battle. This is rather common.
FormChange {
/// The pokemon that changed forms.
pokemon: &'own Pokemon<'battle, 'library>,
/// The new form of the Pokemon.
form: &'own Form,
},
/// This event happens when a Pokemon takes damage.
Damage {
/// The Pokemon that takes damage.
pokemon: &'own Pokemon<'battle, 'library>,
/// The source of damage.
source: DamageSource,
/// The health of the Pokemon before the damage.
original_health: u32,
/// The health of the Pokemon after the damage.
new_health: u32,
},
/// This event happens when a Pokemon faints.
Faint {
/// The pokemon that has fainted.
pokemon: &'own Pokemon<'battle, 'library>,
},
/// This event happens when a Pokemon uses a move on a target, just before any hits.
MoveUse {
/// The data of the move used.
executing_move: &'own ExecutingMove<'own, 'battle, 'library>,
},
/// This event happens when a Pokemon missed.
Miss {
/// The pokemon that missed.
user: &'own Pokemon<'battle, 'library>,
},
/// The turn is finished running, waiting for new input.
EndTurn,
}

View File

@ -1,69 +0,0 @@
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::species_data::form::Form;
use crate::static_data::species_data::species::Species;
use std::fmt::{Debug, Formatter};
#[derive(Default)]
pub struct EventHook {
evt_hook_function: Vec<fn(&Box<&Event>)>,
}
impl<'battle, 'library> EventHook {
pub fn register_listener(&mut self, func: fn(&Box<&Event>)) {
self.evt_hook_function.push(func);
}
pub fn trigger<'b>(&self, evt: Event<'b, 'battle, 'library>) {
let b = Box::new(&evt);
for f in &self.evt_hook_function {
f(&b);
}
}
}
impl Debug for EventHook {
fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
Ok(())
}
}
#[derive(Debug)]
pub enum Event<'own, 'battle, 'library> {
Switch {
side_index: u8,
index: u8,
pokemon: Option<&'own Pokemon<'battle, 'library>>,
},
Swap {
side_index: u8,
index_a: u8,
index_b: u8,
},
SpeciesChange {
pokemon: &'own Pokemon<'battle, 'library>,
species: &'own Species,
form: &'own Form,
},
FormChange {
pokemon: &'own Pokemon<'battle, 'library>,
form: &'own Form,
},
Damage {
pokemon: &'own Pokemon<'battle, 'library>,
source: DamageSource,
original_health: u32,
new_health: u32,
},
Faint {
pokemon: &'own Pokemon<'battle, 'library>,
},
MoveUse {
executing_move: &'own ExecutingMove<'own, 'battle, 'library>,
},
Miss {
user: &'own Pokemon<'battle, 'library>,
},
EndTurn,
}

View File

@ -1 +0,0 @@
pub mod event_hook;

View File

@ -1,35 +1,60 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::Pokemon;
/// The ChoiceQueue is used to run choices one by one.
///
/// It functions internally by holding a vector of choices, and passing ownership of the turn choice
/// to the turn executor one by one, replacing it with empty spots at the start. It holds several
/// helper functions to change the turn order while doing the execution. This is needed, as several
/// moves in Pokemon actively mess with this order.
#[derive(Debug)]
pub struct ChoiceQueue<'battle, 'library> {
/// Our storage of turn choices. Starts out completely filled, then slowly empties as turns get
/// executed.
queue: Vec<Option<TurnChoice<'battle, 'library>>>,
/// The current index of the turn we need to execute next.
current: usize,
}
impl<'battle, 'library> ChoiceQueue<'battle, 'library> {
pub fn new(queue: Vec<Option<TurnChoice<'battle, 'library>>>) -> Self {
/// Initializes a ChoiceQueue. We expect the given queue to already be sorted here.
pub(crate) fn new(queue: Vec<Option<TurnChoice<'battle, 'library>>>) -> Self {
Self { queue, current: 0 }
}
/// Dequeues the next turn choice to be executed. This gives ownership to the callee, and replaces
/// our own reference to the turn choice with an empty spot. It also increments the current position
/// by one.
pub fn dequeue<'b>(&'b mut self) -> TurnChoice<'battle, 'library> {
let c = self.queue[self.current].take();
self.current += 1;
c.unwrap()
}
pub fn peek(&mut self) -> &'battle TurnChoice {
/// This reads what the next choice to execute will be, without modifying state.
pub fn peek(&self) -> &'battle TurnChoice {
self.queue[self.current].as_ref().unwrap()
}
/// Check if we have any choices remaining.
pub fn has_next(&self) -> bool {
self.current < self.queue.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
/// technically already been decided.
pub fn resort(&mut self) {
let len = self.queue.len();
self.queue[self.current..len].sort_unstable_by(|a, b| b.cmp(a));
}
/// This moves the choice of a specific Pokemon up to the next choice to be executed.
pub fn move_pokemon_choice_next(&mut self, _pokemon: &Pokemon) {
todo!()
}
/// Internal helper function to be easily able to iterate over the yet to be executed choices.
pub(crate) fn get_queue(&self) -> &[Option<TurnChoice<'battle, 'library>>] {
&self.queue[self.current..self.queue.len()]
}

View File

@ -1,3 +1,10 @@
pub mod choice_queue;
pub mod target_resolver;
pub mod turn_runner;
#[doc(inline)]
pub use choice_queue::*;
#[doc(inline)]
pub use target_resolver::*;
#[doc(inline)]
pub use turn_runner::*;
mod choice_queue;
mod target_resolver;
mod turn_runner;

View File

@ -1,12 +1,16 @@
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::MoveTarget;
use num_traits::abs;
use std::ops::Deref;
use std::sync::Arc;
use num_traits::abs;
use crate::dynamic_data::Battle;
use crate::dynamic_data::Pokemon;
use crate::static_data::MoveTarget;
/// Helper type for the vector of targets we will return.
pub type TargetList<'own, 'library> = Vec<Option<Arc<Pokemon<'own, 'library>>>>;
/// This returns all Pokemon in the battle.
fn get_all_targets<'b, 'library>(battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> {
let mut v = Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize);
for side in battle.sides() {
@ -17,6 +21,7 @@ fn get_all_targets<'b, 'library>(battle: &Battle<'b, 'library>) -> TargetList<'b
v
}
/// Little helper function to get the other side. 1 --> 0, 0 --> 1
fn get_opposite_side(side: u8) -> u8 {
if side == 0 {
return 1;
@ -24,7 +29,13 @@ fn get_opposite_side(side: u8) -> u8 {
0
}
fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> {
/// Gets all Pokemon that are adjacent to of directly opposite of a Pokemon. This means the target,
/// the Pokemon left of it, the Pokemon right of it, and the Pokemon opposite of it.
fn get_all_adjacent_opponent<'b, 'library>(
side: u8,
index: u8,
battle: &Battle<'b, 'library>,
) -> TargetList<'b, 'library> {
let left = index as i32 - 1;
let right = index + 1;
if left < 0 && right >= battle.pokemon_per_side() {
@ -57,11 +68,9 @@ fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle<'b, 'libr
]
}
fn get_all_adjacent_opponent<'b, 'library>(
side: u8,
index: u8,
battle: &Battle<'b, 'library>,
) -> TargetList<'b, 'library> {
/// Gets all Pokemon that are adjacent to a Pokemon. This includes the target, the pokemon to the
/// left of it, and the Pokemon to the right of it.
fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> {
let left = index as i32 - 1;
let right = index + 1;
if left < 0 && right >= battle.pokemon_per_side() {
@ -86,6 +95,7 @@ fn get_all_adjacent_opponent<'b, 'library>(
]
}
/// Gets the target for a specific move target type, given the targeted position.
pub fn resolve_targets<'b, 'library>(
side: u8,
index: u8,
@ -93,6 +103,8 @@ pub fn resolve_targets<'b, 'library>(
battle: &Battle<'b, 'library>,
) -> TargetList<'b, 'library> {
match target {
// These all resolve to a single position. We let the client deal with where the target is,
// and just return the Pokemon at that given target here.
MoveTarget::Adjacent
| MoveTarget::AdjacentAlly
| MoveTarget::AdjacentAllySelf
@ -102,9 +114,14 @@ pub fn resolve_targets<'b, 'library>(
| MoveTarget::SelfUse => {
vec![battle.get_pokemon(side, index).as_ref().cloned()]
}
// If all pokemon are requested, give all Pokemon on the battlefield
MoveTarget::All => get_all_targets(battle),
// If the adjacent Pokemon are requested, pass those.
MoveTarget::AllAdjacent => get_all_adjacent(side, index, battle),
// If the adjacent and opponent Pokemon are requested, give those.
MoveTarget::AllAdjacentOpponent => get_all_adjacent_opponent(side, index, battle),
// If all Pokemon on a side are requested, simply give all Pokemon on the side back, and let
// the client deal with what side is passed.
MoveTarget::AllAlly | MoveTarget::AllOpponent => {
let mut v = Vec::new();
for pokemon in battle.sides()[side as usize].pokemon().deref() {
@ -115,6 +132,7 @@ pub fn resolve_targets<'b, 'library>(
}
}
/// Checks whether a given side and index are valid for a given movetarget and user.
pub fn is_valid_target(side: u8, index: u8, target: MoveTarget, user: &Pokemon) -> bool {
let user_side = user.get_battle_side_index();
let user_index = user.get_battle_index();

View File

@ -1,17 +1,19 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::event_hook::Event;
use crate::dynamic_data::flow::target_resolver::resolve_targets;
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptWrapper};
use crate::static_data::{DataLibrary, MoveCategory};
use crate::{run_scripts, script_hook, PkmnResult};
use std::ops::Deref;
use std::sync::Arc;
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::Event;
use crate::dynamic_data::flow::target_resolver::resolve_targets;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptWrapper};
use crate::dynamic_data::Battle;
use crate::dynamic_data::DamageSource;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
use crate::static_data::{DataLibrary, MoveCategory};
use crate::{run_scripts, script_hook, PkmnResult};
impl<'own, 'library> Battle<'own, 'library> {
/// Execute the entire turn after the choices are all set.
pub(crate) fn run_turn(&self) -> PkmnResult<()> {
let choice_queue = self.current_turn_queue();
@ -60,20 +62,19 @@ impl<'own, 'library> Battle<'own, 'library> {
}
fn execute_choice(&self, choice: &TurnChoice<'own, 'library>) -> PkmnResult<()> {
// A pass turn choice means the user does not intend to do anything. As such, return.
if let TurnChoice::Pass(..) = choice {
return Ok(());
}
if self.has_ended() {
return Ok(());
}
{
let user = choice.user();
if !user.is_usable() {
return Ok(());
}
if !user.is_on_battlefield() {
return Ok(());
}
let user = choice.user();
if !user.is_usable() {
return Ok(());
}
if !user.is_on_battlefield() {
return Ok(());
}
if !self.can_use(choice) {
return Ok(());

View File

@ -1,2 +0,0 @@
#[derive(Debug)]
pub struct HistoryHolder {}

View File

@ -1 +1,3 @@
pub mod history_holder;
/// The history holder holds all specific history events that happened in the battle.
#[derive(Debug)]
pub struct HistoryHolder {}

View File

@ -1,63 +1,28 @@
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::statistic_set::StatisticSet;
use crate::static_data::statistics::Statistic;
use std::fmt::Debug;
use std::sync::atomic::AtomicU32;
use crate::dynamic_data::Pokemon;
use crate::static_data::Statistic;
use crate::static_data::StatisticSet;
/// A battle stat calculator is used to calculate stats for a pokemon.
pub trait BattleStatCalculator: Debug {
/// Calculate all the flat stats of a Pokemon, disregarding stat boosts.
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>);
/// Calculate a single flat stat of a Pokemon, disregarding stat boost
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
/// Calculate all the boosted stats of a pokemon, including stat boosts.
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>);
/// Calculate a single boosted stat of a Pokemon, including stat boosts.
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32;
}
/// A basic implementation of the Gen 7 stat calculator.
#[derive(Debug)]
pub struct BattleStatCalculator {}
impl BattleStatCalculator {
pub fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>) {
stats.set_stat(Statistic::HP, self.calculate_health_stat(pokemon));
stats.set_stat(Statistic::Attack, self.calculate_other_stat(pokemon, Statistic::Attack));
stats.set_stat(
Statistic::Defense,
self.calculate_other_stat(pokemon, Statistic::Defense),
);
stats.set_stat(
Statistic::SpecialAttack,
self.calculate_other_stat(pokemon, Statistic::SpecialAttack),
);
stats.set_stat(
Statistic::SpecialDefense,
self.calculate_other_stat(pokemon, Statistic::SpecialDefense),
);
stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed));
}
pub fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
if stat == Statistic::HP {
self.calculate_health_stat(pokemon)
} else {
self.calculate_other_stat(pokemon, stat)
}
}
pub fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>) {
stats.set_stat(Statistic::HP, self.calculate_boosted_stat(pokemon, Statistic::HP));
stats.set_stat(
Statistic::Attack,
self.calculate_boosted_stat(pokemon, Statistic::Attack),
);
stats.set_stat(
Statistic::Defense,
self.calculate_boosted_stat(pokemon, Statistic::Defense),
);
stats.set_stat(
Statistic::SpecialAttack,
self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack),
);
stats.set_stat(
Statistic::SpecialDefense,
self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense),
);
stats.set_stat(Statistic::Speed, self.calculate_boosted_stat(pokemon, Statistic::Speed));
}
pub fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
(self.calculate_flat_stat(pokemon, stat) as f32 * self.get_stat_boost_modifier(pokemon, stat)) as u32
}
pub struct Gen7BattleStatCalculator {}
impl Gen7BattleStatCalculator {
/// The calculation used for health points.
fn calculate_health_stat(&self, pokemon: &Pokemon) -> u32 {
let base = pokemon.form().get_base_stat(Statistic::HP) as u32;
let iv = pokemon.individual_values().hp() as u32;
@ -66,6 +31,7 @@ impl BattleStatCalculator {
(((2 * base + iv + (ev / 4)) * level) / 100) + level + 10
}
/// The calculation used for all other stats
fn calculate_other_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
let base = pokemon.form().get_base_stat(stat) as u32;
let iv = pokemon.individual_values().get_stat(stat) as u32;
@ -75,6 +41,7 @@ impl BattleStatCalculator {
return (unmodified as f32 * pokemon.nature().get_stat_modifier(stat)) as u32;
}
/// This functions returns the modifier we need to do to a stat for a given stat boost.
fn get_stat_boost_modifier(&self, pokemon: &Pokemon, stat: Statistic) -> f32 {
let boost = pokemon.stat_boost().get_stat(stat);
match boost {
@ -95,3 +62,56 @@ impl BattleStatCalculator {
}
}
}
impl BattleStatCalculator for Gen7BattleStatCalculator {
fn calculate_flat_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>) {
stats.set_stat(Statistic::HP, self.calculate_health_stat(pokemon));
stats.set_stat(Statistic::Attack, self.calculate_other_stat(pokemon, Statistic::Attack));
stats.set_stat(
Statistic::Defense,
self.calculate_other_stat(pokemon, Statistic::Defense),
);
stats.set_stat(
Statistic::SpecialAttack,
self.calculate_other_stat(pokemon, Statistic::SpecialAttack),
);
stats.set_stat(
Statistic::SpecialDefense,
self.calculate_other_stat(pokemon, Statistic::SpecialDefense),
);
stats.set_stat(Statistic::Speed, self.calculate_other_stat(pokemon, Statistic::Speed));
}
fn calculate_flat_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
if stat == Statistic::HP {
self.calculate_health_stat(pokemon)
} else {
self.calculate_other_stat(pokemon, stat)
}
}
fn calculate_boosted_stats(&self, pokemon: &Pokemon, stats: &StatisticSet<AtomicU32>) {
stats.set_stat(Statistic::HP, self.calculate_boosted_stat(pokemon, Statistic::HP));
stats.set_stat(
Statistic::Attack,
self.calculate_boosted_stat(pokemon, Statistic::Attack),
);
stats.set_stat(
Statistic::Defense,
self.calculate_boosted_stat(pokemon, Statistic::Defense),
);
stats.set_stat(
Statistic::SpecialAttack,
self.calculate_boosted_stat(pokemon, Statistic::SpecialAttack),
);
stats.set_stat(
Statistic::SpecialDefense,
self.calculate_boosted_stat(pokemon, Statistic::SpecialDefense),
);
stats.set_stat(Statistic::Speed, self.calculate_boosted_stat(pokemon, Statistic::Speed));
}
fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
(self.calculate_flat_stat(pokemon, stat) as f32 * self.get_stat_boost_modifier(pokemon, stat)) as u32
}
}

View File

@ -1,9 +1,10 @@
use crate::dynamic_data::models::executing_move::{ExecutingMove, HitData};
use crate::dynamic_data::models::pokemon::Pokemon;
use std::sync::Arc;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::{ExecutingMove, HitData};
use crate::script_hook;
use crate::static_data::{MoveCategory, Statistic};
use std::sync::Arc;
pub trait DamageLibrary: std::fmt::Debug {
fn has_randomness(&self) -> bool;

View File

@ -1,19 +1,20 @@
use std::ops::Deref;
use std::sync::Arc;
use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
use crate::dynamic_data::libraries::damage_library::DamageLibrary;
use crate::dynamic_data::libraries::misc_library::MiscLibrary;
use crate::dynamic_data::libraries::script_resolver::ScriptCategory;
use crate::dynamic_data::script_handling::item_script::ItemScript;
use crate::dynamic_data::script_handling::script::Script;
use crate::static_data::items::item::Item;
use crate::static_data::libraries::static_data::StaticData;
use crate::dynamic_data::ItemScript;
use crate::dynamic_data::Script;
use crate::static_data::Item;
use crate::static_data::StaticData;
use crate::{PkmnResult, StringKey};
use std::ops::Deref;
use std::sync::Arc;
#[derive(Debug)]
pub struct DynamicLibrary {
static_data: StaticData,
stat_calculator: BattleStatCalculator,
stat_calculator: Box<dyn BattleStatCalculator>,
damage_calculator: Box<dyn DamageLibrary>,
misc_library: Box<dyn MiscLibrary<'static>>,
}
@ -25,7 +26,7 @@ unsafe impl Send for DynamicLibrary {}
impl DynamicLibrary {
pub fn new(
static_data: StaticData,
stat_calculator: BattleStatCalculator,
stat_calculator: Box<dyn BattleStatCalculator>,
damage_calculator: Box<dyn DamageLibrary>,
misc_library: Box<dyn MiscLibrary<'static>>,
) -> Self {
@ -40,8 +41,8 @@ impl DynamicLibrary {
pub fn static_data(&self) -> &StaticData {
&self.static_data
}
pub fn stat_calculator(&self) -> &BattleStatCalculator {
&self.stat_calculator
pub fn stat_calculator(&self) -> &dyn BattleStatCalculator {
self.stat_calculator.deref()
}
pub fn damage_calculator(&self) -> &dyn DamageLibrary {
self.damage_calculator.deref()
@ -60,16 +61,15 @@ impl DynamicLibrary {
#[cfg(test)]
pub mod test {
use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
use crate::dynamic_data::libraries::battle_stat_calculator::Gen7BattleStatCalculator;
use crate::dynamic_data::libraries::damage_library::Gen7DamageLibrary;
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use crate::dynamic_data::libraries::misc_library::Gen7MiscLibrary;
use crate::static_data::libraries::static_data;
pub fn build() -> DynamicLibrary {
DynamicLibrary {
static_data: static_data::test::build(),
stat_calculator: BattleStatCalculator {},
static_data: crate::static_data::libraries::static_data::test::build(),
stat_calculator: Box::new(Gen7BattleStatCalculator {}),
damage_calculator: Box::new(Gen7DamageLibrary::new(false)),
misc_library: Box::new(Gen7MiscLibrary::new()),
}

View File

@ -1,16 +1,18 @@
use crate::dynamic_data::choices::{MoveChoice, SwitchChoice, TurnChoice};
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod};
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect};
use crate::{script_hook, StringKey};
use hashbrown::HashSet;
use std::fmt::{Debug, Formatter};
use std::fmt::Debug;
use std::sync::Arc;
pub trait MiscLibrary<'library> {
use hashbrown::HashSet;
use crate::dynamic_data::choices::{MoveChoice, SwitchChoice, TurnChoice};
use crate::dynamic_data::script_handling::ScriptSource;
use crate::dynamic_data::Battle;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
use crate::dynamic_data::{LearnedMove, MoveLearnMethod};
use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect};
use crate::{script_hook, StringKey};
pub trait MiscLibrary<'library>: Debug {
fn is_critical(
&self,
battle: &Battle,
@ -29,12 +31,6 @@ pub trait MiscLibrary<'library> {
// TODO: get time
}
impl<'library> Debug for dyn MiscLibrary<'library> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str("MiscLibrary")
}
}
#[derive(Debug)]
pub struct Gen7MiscLibrary<'library> {
struggle_data: *const MoveData,

View File

@ -1,5 +1,16 @@
pub mod battle_stat_calculator;
pub mod damage_library;
pub mod dynamic_library;
pub mod misc_library;
pub mod script_resolver;
#[doc(inline)]
pub use battle_stat_calculator::*;
#[doc(inline)]
pub use damage_library::*;
#[doc(inline)]
pub use dynamic_library::*;
#[doc(inline)]
pub use misc_library::*;
#[doc(inline)]
pub use script_resolver::*;
mod battle_stat_calculator;
mod damage_library;
pub(crate) mod dynamic_library;
mod misc_library;
mod script_resolver;

View File

@ -1,7 +1,22 @@
pub mod choices;
pub mod event_hooks;
pub mod flow;
pub mod history;
pub mod libraries;
pub mod models;
pub mod script_handling;
#[doc(inline)]
pub use choices::*;
#[doc(inline)]
pub use event_hooks::*;
#[doc(inline)]
pub use flow::*;
#[doc(inline)]
pub use history::*;
#[doc(inline)]
pub use libraries::*;
#[doc(inline)]
pub use models::*;
#[doc(inline)]
pub use script_handling::*;
mod choices;
mod event_hooks;
mod flow;
mod history;
pub(crate) mod libraries;
mod models;
mod script_handling;

View File

@ -1,24 +1,26 @@
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
use atomic::Atomic;
use parking_lot::RwLock;
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::event_hook::{Event, EventHook};
use crate::dynamic_data::flow::choice_queue::ChoiceQueue;
use crate::dynamic_data::flow::target_resolver::is_valid_target;
use crate::dynamic_data::history::history_holder::HistoryHolder;
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use crate::dynamic_data::event_hooks::{Event, EventHook};
use crate::dynamic_data::history::HistoryHolder;
use crate::dynamic_data::is_valid_target;
use crate::dynamic_data::models::battle_party::BattleParty;
use crate::dynamic_data::models::battle_random::BattleRandom;
use crate::dynamic_data::models::battle_result::BattleResult;
use crate::dynamic_data::models::battle_side::BattleSide;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::script::Script;
use crate::dynamic_data::script_handling::script_set::ScriptSet;
use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::{script_hook, PkmnResult, ScriptCategory, StringKey};
use atomic::Atomic;
use parking_lot::RwLock;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
use crate::dynamic_data::ChoiceQueue;
use crate::dynamic_data::DynamicLibrary;
use crate::dynamic_data::Script;
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScripts;
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::{script_hook, PkmnResult, StringKey};
#[derive(Debug)]
pub struct Battle<'own, 'library> {

View File

@ -1,10 +1,11 @@
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::script_hook;
use crate::utils::random::Random;
use std::fmt::{Debug, Formatter};
use std::sync::{Arc, Mutex};
use crate::utils::Random;
#[derive(Default)]
pub struct BattleRandom {

View File

@ -1,19 +1,21 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::event_hook::Event;
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::battle_party::BattleParty;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::script::Script;
use crate::dynamic_data::script_handling::script_set::ScriptSet;
use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::{script_hook, PkmnResult, StringKey};
use parking_lot::lock_api::RwLockReadGuard;
use parking_lot::{RawRwLock, RwLock};
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use std::sync::Arc;
use parking_lot::lock_api::RwLockReadGuard;
use parking_lot::{RawRwLock, RwLock};
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::Event;
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::battle_party::BattleParty;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::Script;
use crate::dynamic_data::ScriptSet;
use crate::dynamic_data::VolatileScripts;
use crate::{script_hook, PkmnResult, StringKey};
#[derive(Debug)]
pub struct BattleSide<'own, 'library> {
index: u8,

View File

@ -1,16 +1,18 @@
use crate::dynamic_data::flow::target_resolver::TargetList;
use crate::dynamic_data::models::learned_move::LearnedMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::script::ScriptContainer;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::static_data::MoveData;
use crate::{PkmnResult, PokemonError};
use atomic::Atomic;
use parking_lot::RwLock;
use std::ops::Deref;
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU8, Ordering};
use std::sync::Arc;
use atomic::Atomic;
use parking_lot::RwLock;
use crate::dynamic_data::models::learned_move::LearnedMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::ScriptContainer;
use crate::dynamic_data::TargetList;
use crate::static_data::MoveData;
use crate::{PkmnResult, PokemonError};
#[derive(Default, Debug)]
pub struct HitData {
critical: AtomicBool,

View File

@ -1,11 +1,34 @@
pub mod battle;
pub mod battle_party;
pub mod battle_random;
pub mod battle_result;
pub mod battle_side;
pub mod damage_source;
pub mod executing_move;
pub mod learned_move;
pub mod pokemon;
pub mod pokemon_builder;
pub mod pokemon_party;
#[doc(inline)]
pub use battle::*;
#[doc(inline)]
pub use battle_party::*;
#[doc(inline)]
pub use battle_random::*;
#[doc(inline)]
pub use battle_result::*;
#[doc(inline)]
pub use battle_side::*;
#[doc(inline)]
pub use damage_source::*;
#[doc(inline)]
pub use executing_move::*;
#[doc(inline)]
pub use learned_move::*;
#[doc(inline)]
pub use pokemon::*;
#[doc(inline)]
pub use pokemon_builder::*;
#[doc(inline)]
pub use pokemon_party::*;
mod battle;
mod battle_party;
mod battle_random;
mod battle_result;
mod battle_side;
mod damage_source;
mod executing_move;
mod learned_move;
mod pokemon;
mod pokemon_builder;
mod pokemon_party;

View File

@ -1,30 +1,29 @@
use crate::defines::{LevelInt, MAX_MOVES};
use crate::dynamic_data::event_hooks::event_hook::Event;
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod};
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::dynamic_data::script_handling::script_set::ScriptSet;
use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts;
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::static_data::items::item::Item;
use crate::static_data::natures::Nature;
use crate::static_data::species_data::ability::Ability;
use crate::static_data::species_data::ability_index::AbilityIndex;
use crate::static_data::species_data::form::Form;
use crate::static_data::species_data::gender::Gender;
use crate::static_data::species_data::species::Species;
use crate::static_data::statistic_set::{ClampedStatisticSet, StatisticSet};
use crate::static_data::DataLibrary;
use crate::utils::random::Random;
use crate::{script_hook, PkmnResult, ScriptCategory, StringKey};
use atomic::Atomic;
use parking_lot::RwLock;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicI8, AtomicU32, AtomicU8, Ordering};
use std::sync::{Arc, Weak};
use atomic::Atomic;
use parking_lot::RwLock;
use crate::defines::{LevelInt, MAX_MOVES};
use crate::dynamic_data::event_hooks::Event;
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod};
use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper};
use crate::dynamic_data::{DynamicLibrary, Script, ScriptCategory, ScriptContainer, ScriptSet, VolatileScripts};
use crate::static_data::Ability;
use crate::static_data::AbilityIndex;
use crate::static_data::DataLibrary;
use crate::static_data::Form;
use crate::static_data::Gender;
use crate::static_data::Item;
use crate::static_data::Nature;
use crate::static_data::Species;
use crate::static_data::{ClampedStatisticSet, StatisticSet};
use crate::utils::Random;
use crate::{script_hook, PkmnResult, StringKey};
#[derive(Debug)]
pub struct PokemonBattleData<'pokemon, 'library> {
battle: *mut Battle<'pokemon, 'library>,
@ -585,15 +584,15 @@ impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> {
#[cfg(test)]
pub mod test {
use crate::dynamic_data::libraries::dynamic_library;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::libraries::data_library::DataLibrary;
use crate::static_data::species_data::ability_index::AbilityIndex;
use crate::static_data::species_data::gender::Gender;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::AbilityIndex;
use crate::static_data::DataLibrary;
use crate::static_data::Gender;
#[test]
fn construct_pokemon() {
let lib = dynamic_library::test::build();
let lib = crate::dynamic_data::libraries::dynamic_library::test::build();
let species = lib.static_data().species().get(&"foo".into()).unwrap();
let form = species.get_form(&"default".into()).unwrap();

View File

@ -1,7 +1,7 @@
use crate::defines::LevelInt;
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use crate::dynamic_data::models::learned_move::MoveLearnMethod;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::DynamicLibrary;
use crate::static_data::{AbilityIndex, DataLibrary, Gender};
use crate::StringKey;

View File

@ -1,12 +1,20 @@
use crate::dynamic_data::script_handling::script::{Script, ScriptContainer};
use crate::dynamic_data::script_handling::script_set::ScriptSet;
use parking_lot::RwLock;
use std::sync::{Arc, Weak};
pub mod item_script;
pub mod script;
pub mod script_set;
pub mod volatile_scripts;
use parking_lot::RwLock;
#[doc(inline)]
pub use item_script::*;
#[doc(inline)]
pub use script::*;
#[doc(inline)]
pub use script_set::*;
#[doc(inline)]
pub use volatile_scripts::*;
mod item_script;
mod script;
mod script_set;
mod volatile_scripts;
#[macro_export]
macro_rules! script_hook {
@ -25,21 +33,6 @@ macro_rules! script_hook {
};
}
#[macro_export]
macro_rules! script_hook_on_lock {
($hook_name: ident, $source: ident, $($parameters: expr),*) => {
let mut aggregator = $source.read().get_script_iterator();
while let Some(script) = aggregator.get_next() {
let lock = &mut script.get().read();
let script = lock.as_mut().unwrap();
if script.is_suppressed() {
continue;
}
script.$hook_name($($parameters),*);
}
};
}
#[macro_export]
macro_rules! run_scripts {
($hook_name: ident, $source: ident, $($parameters: expr),*) => {
@ -198,12 +191,14 @@ impl ScriptAggregator {
#[cfg(test)]
mod tests {
use super::*;
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::dynamic_data::script_handling::script::ScriptContainer;
use crate::static_data::EffectParameter;
use crate::StringKey;
use std::any::Any;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use super::*;
pub struct TestScript {
name: StringKey,

View File

@ -1,12 +1,3 @@
use crate::dynamic_data::choices::{MoveChoice, TurnChoice};
use crate::dynamic_data::models::battle::Battle;
use crate::dynamic_data::models::damage_source::DamageSource;
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::moves::secondary_effect::EffectParameter;
use crate::static_data::{Item, Statistic};
use crate::StringKey;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use std::any::Any;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
@ -14,6 +5,17 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread::JoinHandle;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard};
use crate::dynamic_data::choices::{MoveChoice, TurnChoice};
use crate::dynamic_data::Battle;
use crate::dynamic_data::DamageSource;
use crate::dynamic_data::ExecutingMove;
use crate::dynamic_data::Pokemon;
use crate::static_data::EffectParameter;
use crate::static_data::{Item, Statistic};
use crate::StringKey;
pub trait Script: Send + Sync {
fn name(&self) -> &StringKey;
fn get_marked_for_deletion(&self) -> &AtomicBool;
@ -231,9 +233,10 @@ impl Clone for ScriptContainer {
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicBool, AtomicPtr};
use super::*;
pub struct TestScript {
name: StringKey,
container: AtomicPtr<ScriptContainer>,

View File

@ -1,6 +1,8 @@
// The too many arguments is annoying, especially for when we create constructors, disable.
// The too many arguments is annoying, especially for when we create constructor, disable.
#![allow(clippy::too_many_arguments, clippy::needless_range_loop)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
#![feature(test)]
#![feature(bench_black_box)]
#![feature(let_chains)]
@ -8,22 +10,45 @@
#![feature(const_option)]
#![feature(is_some_with)]
//! PkmnLib
//! PkmnLib is a full featured implementation of Pokemon. while currently focused on implementing
//! generation 7, this library tries to offload generational differences such as move effects
//! to a scripting library.
//!
extern crate core;
use crate::dynamic_data::libraries::script_resolver::ScriptCategory;
#[doc(hidden)]
pub use utils::*;
use crate::dynamic_data::ScriptCategory;
/// The defines module holds the core defines of the library
pub mod defines;
/// The dynamic data module holds data that can change during execution, and things that relate to
/// this. This includes things as Pokemon themselves, battles, etc.
pub mod dynamic_data;
/// The static data module holds data that can be set once, and then never change. This includes
/// things such as data about Pokemon species, data about items, etc.
pub mod static_data;
/// The utils module includes misc utils that are used within PkmnLib
pub mod utils;
/// The PokemonError enum holds all different error states that can be encountered in PkmnLib.
#[derive(Debug, Clone)]
pub enum PokemonError {
ScriptNotFound { category: ScriptCategory, name: String },
MiscError,
/// A script was requested, but we were unable to find it.
ScriptNotFound {
/// The category of the script we requested,
category: ScriptCategory,
/// The unique key of the requested script.
name: StringKey,
},
/// We requested data for a specific target, but that target does not exist on the battle field.
InvalidTargetRequested,
/// Misc errors. Use of this should be minimized, but it is useful for early development.
MiscError,
}
/// A simple result type.
pub type PkmnResult<T> = Result<T, PokemonError>;
pub use utils::*;

View File

@ -1,5 +1,9 @@
use crate::defines::LevelInt;
use crate::static_data::growth_rates::growth_rate::GrowthRate;
pub trait GrowthRate {
fn calculate_level(&self, experience: u32) -> LevelInt;
fn calculate_experience(&self, level: LevelInt) -> u32;
}
pub struct LookupGrowthRate {
experience: Vec<u32>,

View File

@ -1,6 +0,0 @@
use crate::defines::LevelInt;
pub trait GrowthRate {
fn calculate_level(&self, experience: u32) -> LevelInt;
fn calculate_experience(&self, level: LevelInt) -> u32;
}

View File

@ -1,5 +0,0 @@
pub mod growth_rate;
pub mod lookup_growth_rate;
pub use growth_rate::*;
pub use lookup_growth_rate::*;

View File

@ -1,6 +1,33 @@
use super::item_category::{BattleItemCategory, ItemCategory};
use crate::StringKey;
use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::StringKey;
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum ItemCategory {
MiscItem,
Pokeball,
Medicine,
Berry,
TMHM,
FormChanger,
KeyItem,
Mail,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum BattleItemCategory {
None,
Healing,
StatusHealing,
Pokeball,
MiscBattleItem,
}
#[derive(Debug)]
pub struct Item {

View File

@ -1,27 +0,0 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum ItemCategory {
MiscItem,
Pokeball,
Medicine,
Berry,
TMHM,
FormChanger,
KeyItem,
Mail,
}
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(u8)]
pub enum BattleItemCategory {
None,
Healing,
StatusHealing,
Pokeball,
MiscBattleItem,
}

View File

@ -1,5 +0,0 @@
pub mod item;
pub mod item_category;
pub use item::*;
pub use item_category::*;

View File

@ -1,7 +1,8 @@
use hashbrown::HashMap;
use crate::static_data::Ability;
use crate::static_data::DataLibrary;
use crate::StringKey;
use hashbrown::HashMap;
#[derive(Debug)]
pub struct AbilityLibrary {
@ -34,9 +35,9 @@ impl DataLibrary<'_, Box<Ability>> for AbilityLibrary {
#[cfg(test)]
pub mod tests {
use crate::static_data::libraries::ability_library::AbilityLibrary;
use crate::static_data::libraries::data_library::DataLibrary;
use crate::static_data::species_data::ability::Ability;
use crate::static_data::Ability;
use crate::static_data::AbilityLibrary;
use crate::static_data::DataLibrary;
use crate::StringKey;
pub fn build() -> AbilityLibrary {

View File

@ -1,9 +1,11 @@
use std::fmt;
use std::fmt::{Debug, Formatter};
use hashbrown::HashMap;
use crate::defines::LevelInt;
use crate::static_data::GrowthRate;
use crate::StringKey;
use hashbrown::HashMap;
use std::fmt;
use std::fmt::{Debug, Formatter};
pub struct GrowthRateLibrary {
growth_rates: HashMap<StringKey, Box<dyn GrowthRate>>,
@ -35,7 +37,7 @@ impl Debug for GrowthRateLibrary {
#[cfg(test)]
pub mod tests {
use crate::static_data::growth_rates::lookup_growth_rate::LookupGrowthRate;
use crate::static_data::growth_rates::LookupGrowthRate;
use crate::static_data::libraries::growth_rate_library::GrowthRateLibrary;
pub fn build() -> GrowthRateLibrary {

View File

@ -1,7 +1,8 @@
use hashbrown::HashMap;
use crate::static_data::DataLibrary;
use crate::static_data::Item;
use crate::StringKey;
use hashbrown::HashMap;
#[derive(Debug)]
pub struct ItemLibrary {
@ -34,11 +35,12 @@ impl DataLibrary<'_, Box<Item>> for ItemLibrary {
#[cfg(test)]
pub mod tests {
use crate::static_data::items::item::Item;
use crate::static_data::items::item_category::{BattleItemCategory, ItemCategory};
use hashbrown::HashSet;
use crate::static_data::libraries::data_library::DataLibrary;
use crate::static_data::libraries::item_library::ItemLibrary;
use hashbrown::HashSet;
use crate::static_data::Item;
use crate::static_data::{BattleItemCategory, ItemCategory};
fn build_item() -> Item {
Item::new(

View File

@ -1,19 +1,28 @@
pub mod ability_library;
pub mod data_library;
pub mod growth_rate_library;
pub mod item_library;
pub mod library_settings;
pub mod move_library;
pub mod species_library;
pub mod static_data;
pub mod type_library;
#[doc(inline)]
pub use ability_library::AbilityLibrary;
#[doc(inline)]
pub use data_library::DataLibrary;
#[doc(inline)]
pub use growth_rate_library::GrowthRateLibrary;
#[doc(inline)]
pub use item_library::ItemLibrary;
#[doc(inline)]
pub use library_settings::LibrarySettings;
#[doc(inline)]
pub use move_library::MoveLibrary;
#[doc(inline)]
pub use species_library::SpeciesLibrary;
#[doc(inline)]
pub use static_data::StaticData;
#[doc(inline)]
pub use type_library::TypeLibrary;
mod ability_library;
mod data_library;
mod growth_rate_library;
mod item_library;
mod library_settings;
mod move_library;
mod species_library;
pub(crate) mod static_data;
mod type_library;

View File

@ -1,7 +1,8 @@
use hashbrown::HashMap;
use crate::static_data::DataLibrary;
use crate::static_data::MoveData;
use crate::StringKey;
use hashbrown::HashMap;
#[derive(Debug)]
pub struct MoveLibrary {
@ -34,11 +35,12 @@ impl DataLibrary<'_, MoveData> for MoveLibrary {
#[cfg(test)]
pub mod tests {
use hashbrown::HashSet;
use crate::static_data::libraries::data_library::DataLibrary;
use crate::static_data::libraries::move_library::MoveLibrary;
use crate::static_data::moves::move_data::{MoveCategory, MoveData, MoveTarget};
use crate::static_data::{MoveCategory, MoveData, MoveTarget};
use crate::StringKey;
use hashbrown::HashSet;
fn build_move() -> MoveData {
MoveData::new(

View File

@ -1,7 +1,8 @@
use hashbrown::HashMap;
use crate::static_data::DataLibrary;
use crate::static_data::Species;
use crate::StringKey;
use hashbrown::HashMap;
#[derive(Debug)]
pub struct SpeciesLibrary {
@ -34,13 +35,14 @@ impl<'a> DataLibrary<'a, Box<Species>> for SpeciesLibrary {
#[cfg(test)]
pub mod tests {
use hashbrown::HashSet;
use crate::static_data::libraries::data_library::DataLibrary;
use crate::static_data::libraries::species_library::SpeciesLibrary;
use crate::static_data::species_data::form::Form;
use crate::static_data::species_data::learnable_moves::LearnableMoves;
use crate::static_data::species_data::species::Species;
use crate::static_data::Form;
use crate::static_data::LearnableMoves;
use crate::static_data::Species;
use crate::static_data::StaticStatisticSet;
use hashbrown::HashSet;
fn build_species() -> Species {
Species::new(

View File

@ -1,17 +1,25 @@
pub mod growth_rates;
pub mod items;
pub mod libraries;
pub mod moves;
pub mod natures;
pub mod species_data;
pub mod statistic_set;
pub mod statistics;
#[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::*;
mod growth_rates;
mod items;
pub(crate) mod libraries;
mod moves;
mod natures;
mod species_data;
mod statistic_set;
mod statistics;

View File

@ -1,9 +1,7 @@
pub mod move_data;
pub mod secondary_effect;
#[doc(inline)]
pub use move_data::*;
#[doc(inline)]
pub use secondary_effect::*;
pub use move_data::MoveCategory;
pub use move_data::MoveData;
pub use move_data::MoveTarget;
pub use secondary_effect::EffectParameter;
pub use secondary_effect::SecondaryEffect;
mod move_data;
mod secondary_effect;

View File

@ -1,13 +1,19 @@
pub mod ability;
pub mod ability_index;
pub mod form;
pub mod gender;
pub mod learnable_moves;
pub mod species;
#[doc(inline)]
pub use ability::Ability;
#[doc(inline)]
pub use ability_index::AbilityIndex;
#[doc(inline)]
pub use form::Form;
#[doc(inline)]
pub use gender::Gender;
#[doc(inline)]
pub use learnable_moves::LearnableMoves;
#[doc(inline)]
pub use species::Species;
mod ability;
mod ability_index;
mod form;
mod gender;
mod learnable_moves;
mod species;

View File

@ -1,18 +1,30 @@
use super::statistics::Statistic;
use atomic_prim_traits::AtomicInt;
use num_traits::{clamp, NumCast, PrimInt};
use std::sync::atomic::Ordering;
use atomic_prim_traits::AtomicInt;
use num_traits::{clamp, NumCast, PrimInt};
use super::statistics::Statistic;
/// A collection of every individual stat. This set can hold any value that is valid for its integer
/// type, and can be modified at will.
///
/// As all data in this type is atomic, threaded access to this struct is completely legal.
#[derive(Default, Eq, PartialEq, Clone, Debug)]
pub struct StatisticSet<T>
where
T: AtomicInt,
{
/// The health point stat value.
hp: T,
/// The physical attack stat value.
attack: T,
/// The physical defense stat value.
defense: T,
/// The special attack stat value.
special_attack: T,
/// The special defense stat value.
special_defense: T,
/// The speed stat value.
speed: T,
}
@ -20,6 +32,7 @@ impl<T> StatisticSet<T>
where
T: AtomicInt,
{
/// Creates a new statistic set with given stats.
pub fn new(
hp: T::Prim,
attack: T::Prim,
@ -38,25 +51,32 @@ where
}
}
/// The health point stat value.
pub fn hp(&self) -> T::Prim {
self.hp.load(Ordering::Relaxed)
}
/// The physical attack stat value.
pub fn attack(&self) -> T::Prim {
self.attack.load(Ordering::Relaxed)
}
/// The physical defense stat value.
pub fn defense(&self) -> T::Prim {
self.defense.load(Ordering::Relaxed)
}
/// The special attack stat value.
pub fn special_attack(&self) -> T::Prim {
self.special_attack.load(Ordering::Relaxed)
}
/// The special defense stat value.
pub fn special_defense(&self) -> T::Prim {
self.special_defense.load(Ordering::Relaxed)
}
/// The speed stat value.
pub fn speed(&self) -> T::Prim {
self.speed.load(Ordering::Relaxed)
}
/// Get the value of a specific stat
pub fn get_stat(&self, stat: Statistic) -> T::Prim {
match stat {
Statistic::HP => self.hp.load(Ordering::Relaxed),
@ -68,6 +88,7 @@ where
}
}
/// Modify the value of a specific stat.
pub fn set_stat(&self, stat: Statistic, value: T::Prim) {
match stat {
Statistic::HP => self.hp.store(value, Ordering::SeqCst),
@ -79,6 +100,7 @@ where
}
}
/// Increase the value of a given stat by a value.
pub fn increase_stat(&self, stat: Statistic, value: T::Prim) {
match stat {
Statistic::HP => self.hp.fetch_add(value, Ordering::SeqCst),
@ -90,6 +112,7 @@ where
};
}
/// Decrease the value of a given stat by a value.
pub fn decrease_stat(&self, stat: Statistic, value: T::Prim) {
match stat {
Statistic::HP => self.hp.fetch_sub(value, Ordering::SeqCst),
@ -102,16 +125,25 @@ where
}
}
/// A collection of statistics that can not be modified after creation.
///
/// As no modifications happen, this struct does not use atomics.
#[derive(Default, Eq, PartialEq, Clone, Debug)]
pub struct StaticStatisticSet<T>
where
T: PrimInt,
{
/// The health point stat value.
hp: T,
/// The physical attack stat value.
attack: T,
/// The physical defense stat value.
defense: T,
/// The special attack stat value.
special_attack: T,
/// The special defense stat value.
special_defense: T,
/// The speed stat value.
speed: T,
}
@ -119,6 +151,7 @@ impl<T> StaticStatisticSet<T>
where
T: PrimInt,
{
/// Create a new static statistic set.
pub const fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
hp,
@ -130,25 +163,32 @@ where
}
}
/// The health point stat value.=
pub const fn hp(&self) -> T {
self.hp
}
/// The physical attack stat value.
pub const fn attack(&self) -> T {
self.attack
}
/// The physical defense stat value.
pub const fn defense(&self) -> T {
self.defense
}
/// The special attack stat value.
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
}
/// The speed stat value.
pub const fn speed(&self) -> T {
self.speed
}
/// Get the value of a specific stat
pub const fn get_stat(&self, stat: Statistic) -> T {
match stat {
Statistic::HP => self.hp,
@ -166,14 +206,22 @@ pub struct ClampedStatisticSet<T, const MIN: i64, const MAX: i64>
where
T: AtomicInt,
{
/// The health point stat value.
hp: T,
/// The physical attack stat value.
attack: T,
/// The physical defense stat value.
defense: T,
/// The special attack stat value.
special_attack: T,
/// The special defense stat value.
special_defense: T,
/// The speed stat value.
speed: T,
}
/// A clamped statistic set is a collection of each statistics that can be modified, but that will
/// always remain between two compile time constant values (Min, Max).
impl<T, const MIN: i64, const MAX: i64> ClampedStatisticSet<T, MIN, MAX>
where
T: AtomicInt,
@ -302,9 +350,10 @@ where
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::AtomicI32;
use super::*;
#[test]
fn create_get_values() {
let set = StatisticSet::<AtomicI32>::new(1, 2, 3, 4, 5, 6);

View File

@ -1,5 +1,7 @@
pub mod random;
pub mod string_key;
#[doc(inline)]
pub use random::Random;
#[doc(inline)]
pub use string_key::StringKey;
mod random;
mod string_key;

View File

@ -1,6 +1,7 @@
use pkmn_lib::dynamic_data::models::battle::Battle;
use serde::Deserialize;
use pkmn_lib::dynamic_data::Battle;
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TestDataGetter {

View File

@ -1,22 +1,24 @@
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fs::File;
use std::io::Read;
use hashbrown::HashSet;
use num_traits::PrimInt;
use project_root::get_project_root;
use serde_json::Value;
use pkmn_lib::defines::LevelInt;
use pkmn_lib::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
use pkmn_lib::dynamic_data::libraries::damage_library::Gen7DamageLibrary;
use pkmn_lib::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use pkmn_lib::dynamic_data::libraries::misc_library::Gen7MiscLibrary;
use pkmn_lib::dynamic_data::DynamicLibrary;
use pkmn_lib::dynamic_data::Gen7BattleStatCalculator;
use pkmn_lib::dynamic_data::Gen7DamageLibrary;
use pkmn_lib::dynamic_data::Gen7MiscLibrary;
use pkmn_lib::static_data::{
Ability, AbilityLibrary, BattleItemCategory, DataLibrary, EffectParameter, Form, GrowthRateLibrary, Item,
ItemLibrary, LearnableMoves, LibrarySettings, LookupGrowthRate, MoveData, MoveLibrary, Nature, NatureLibrary,
SecondaryEffect, Species, StaticData, StaticStatisticSet, Statistic, TypeLibrary,
};
use pkmn_lib::StringKey;
use project_root::get_project_root;
use serde_json::Value;
use std::convert::TryFrom;
use std::fmt::Debug;
use std::fs::File;
use std::io::Read;
pub fn load_library() -> DynamicLibrary {
let mut path = get_project_root().unwrap();
@ -32,7 +34,7 @@ pub fn load_library() -> DynamicLibrary {
load_species(&path, &mut data);
let dynamic = DynamicLibrary::new(
data,
BattleStatCalculator {},
Box::new(Gen7BattleStatCalculator {}),
Box::new(Gen7DamageLibrary::new(false)),
Box::new(Gen7MiscLibrary::new()),
);

View File

@ -1,15 +1,18 @@
use super::test_step::TestStep;
use pkmn_lib::defines::LevelInt;
use pkmn_lib::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use pkmn_lib::dynamic_data::models::battle::Battle;
use pkmn_lib::dynamic_data::models::battle_party::BattleParty;
use pkmn_lib::dynamic_data::models::pokemon::Pokemon;
use pkmn_lib::dynamic_data::models::pokemon_builder::PokemonBuilder;
use pkmn_lib::dynamic_data::models::pokemon_party::PokemonParty;
use pkmn_lib::StringKey;
use serde::Deserialize;
use std::sync::Arc;
use serde::Deserialize;
use pkmn_lib::defines::LevelInt;
use pkmn_lib::dynamic_data::Battle;
use pkmn_lib::dynamic_data::BattleParty;
use pkmn_lib::dynamic_data::DynamicLibrary;
use pkmn_lib::dynamic_data::Pokemon;
use pkmn_lib::dynamic_data::PokemonBuilder;
use pkmn_lib::dynamic_data::PokemonParty;
use pkmn_lib::StringKey;
use super::test_step::TestStep;
#[derive(Deserialize)]
pub struct TestCase {
pub name: String,

View File

@ -1,9 +1,11 @@
use super::data_getter::TestDataGetter;
use pkmn_lib::dynamic_data::choices::{MoveChoice, PassChoice, TurnChoice};
use pkmn_lib::dynamic_data::models::battle::Battle;
use pkmn_lib::StringKey;
use serde::Deserialize;
use pkmn_lib::dynamic_data::Battle;
use pkmn_lib::dynamic_data::{MoveChoice, PassChoice, TurnChoice};
use pkmn_lib::StringKey;
use super::data_getter::TestDataGetter;
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TestStep {

View File

@ -2,13 +2,15 @@
#![feature(once_cell)]
#![test_runner(datatest::runner)]
use crate::common::{library_loader, TestCase};
use pkmn_lib::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use std::fs::File;
use std::io::Read;
use std::lazy::SyncLazy;
use std::path::Path;
use pkmn_lib::dynamic_data::DynamicLibrary;
use crate::common::{library_loader, TestCase};
pub mod common;
static LIBRARY: SyncLazy<DynamicLibrary> = SyncLazy::new(|| {