Loads more work on battling, initial stretch to run a turn done.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2022-06-16 17:59:33 +02:00
parent a33369afcc
commit ff541b0696
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
50 changed files with 105871 additions and 497 deletions

View File

@ -6,10 +6,13 @@ edition = "2018"
[lib]
name = "pkmn_lib"
crate_type = ["cdylib"]
crate_type = ["rlib"]
path = "src/lib.rs"
[features]
c_interface = []
serde = ["dep:serde", "dep:serde_json"]
default = ["serde"]
[profile.dev]
opt-level = 0
@ -43,11 +46,15 @@ chrono = "0.4.19"
# Used for RNG
rand = "0.8.5"
rand_pcg = "0.3.1"
# Used for the hashmap!{} macro, creating a simple initializer pattern for hashmaps.
maplit = "1.0.2"
failure = "0.1.8"
failure_derive = "0.1.8"
lazy_static = "1.4.0"
hashbrown = "0.12.1"
indexmap = "1.8.2"
parking_lot = "0.12.1"
parking_lot = "0.12.1"
serde = { version = "1.0.137", optional = true, features = ["derive"] }
serde_json = { version = "1.0.81", optional = true }
[dev-dependencies]
csv = "1.1.6"
project-root = "0.2.2"
syn = "1.0.96"

5
lifetime_notes.txt Normal file
View File

@ -0,0 +1,5 @@
Main lifetimes:
- Library: the static data underlying everything. This has the longest lifetime.
- Pokemon: The lifetime of a Pokemon.
- Party: The lifetime of a party, as a Pokemon can be added or taken from a party, this is shorter than the lifetime of a pokemon
- Battle: The lifetime of a battle.

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 120

View File

@ -249,9 +249,7 @@ impl<T: 'static> Deref for DeferredCleanup<T> {
type Target = T;
fn deref(&self) -> &T {
self.with_value(
|value| unsafe_block!("The borrow of self protects the inner value" => &*value.get()),
)
self.with_value(|value| unsafe_block!("The borrow of self protects the inner value" => &*value.get()))
}
}

View File

@ -36,10 +36,7 @@ enum Kind {
impl PkmnResult {
pub(super) fn ok() -> Self {
PkmnResult {
kind: Kind::Ok,
id: 0,
}
PkmnResult { kind: Kind::Ok, id: 0 }
}
pub(super) fn argument_null() -> Self {
@ -65,10 +62,7 @@ impl PkmnResult {
}
pub(super) fn context(self, e: impl Fail) -> Self {
assert!(
self.as_err().is_some(),
"context can only be attached to errors"
);
assert!(self.as_err().is_some(), "context can only be attached to errors");
let err = Some(format_error(&e));
@ -105,8 +99,7 @@ impl PkmnResult {
.value
}
Err(e) => {
let extract_panic =
|| extract_panic(&e).map(|s| format!("internal panic with '{}'", s));
let extract_panic = || extract_panic(&e).map(|s| format!("internal panic with '{}'", s));
// Set the last error to the panic message if it's not already set
last_result
@ -124,9 +117,7 @@ impl PkmnResult {
})
}
pub(super) fn with_last_result<R>(
f: impl FnOnce(Option<(PkmnResult, Option<&str>)>) -> R,
) -> R {
pub(super) fn with_last_result<R>(f: impl FnOnce(Option<(PkmnResult, Option<&str>)>) -> R) -> R {
LAST_RESULT.with(|last_result| {
let last_result = last_result.borrow();

View File

@ -1,32 +1,359 @@
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;
#[derive(Debug)]
pub enum TurnChoice<'a> {
Move {
user: &'a Pokemon<'a>,
used_move: Box<LearnedMove<'a>>,
target_side: u8,
target_index: u8,
},
Item {
user: &'a Pokemon<'a>,
},
Switch {
user: &'a Pokemon<'a>,
},
Flee {
user: &'a Pokemon<'a>,
},
struct CommonChoiceData<'user, 'library> {
user: Arc<RwLock<Pokemon<'user, 'library>>>,
speed: u32,
random_value: u32,
has_failed: bool,
script_source_data: RwLock<ScriptSourceData>,
}
impl<'a> TurnChoice<'a> {
pub fn user(&self) -> &'a Pokemon<'a> {
#[derive(Debug)]
pub enum TurnChoice<'user, 'library> {
Move(MoveChoice<'user, 'library>),
Item(ItemChoice<'user, 'library>),
Switch(SwitchChoice<'user, 'library>),
Flee(FleeChoice<'user, 'library>),
Pass(PassChoice<'user, 'library>),
}
impl<'user, 'library> TurnChoice<'user, 'library> {
fn choice_data(&self) -> &CommonChoiceData<'user, 'library> {
match self {
TurnChoice::Move { user, .. } => user,
TurnChoice::Item { user, .. } => user,
TurnChoice::Switch { user, .. } => user,
TurnChoice::Flee { user, .. } => user,
TurnChoice::Move(data) => &data.choice_data,
TurnChoice::Item(data) => &data.choice_data,
TurnChoice::Switch(data) => &data.choice_data,
TurnChoice::Flee(data) => &data.choice_data,
TurnChoice::Pass(data) => &data.choice_data,
}
}
fn choice_data_mut(&mut self) -> &mut Box<CommonChoiceData<'user, 'library>> {
match self {
TurnChoice::Move(data) => &mut data.choice_data,
TurnChoice::Item(data) => &mut data.choice_data,
TurnChoice::Switch(data) => &mut data.choice_data,
TurnChoice::Flee(data) => &mut data.choice_data,
TurnChoice::Pass(data) => &mut data.choice_data,
}
}
pub fn user(&self) -> &Arc<RwLock<Pokemon<'user, 'library>>> {
&self.choice_data().user
}
pub fn speed(&self) -> u32 {
self.choice_data().speed
}
pub fn speed_mut(&mut self) -> &mut u32 {
&mut self.choice_data_mut().speed
}
pub fn has_failed(&self) -> bool {
self.choice_data().has_failed
}
pub fn fail(&mut self) {
self.choice_data_mut().has_failed = true
}
pub(crate) fn random_value(&self) -> u32 {
self.choice_data().random_value
}
pub(crate) fn set_random_value(&mut self, val: u32) {
self.choice_data_mut().random_value = val;
}
pub(crate) fn get_move_turn_data<'b>(&'b mut self) -> &'b mut MoveChoice<'user, 'library> {
if let TurnChoice::Move(data) = self {
return data;
}
panic!("Invalid turn choice");
}
}
impl<'user, 'library> ScriptSource<'user> for TurnChoice<'user, 'library> {
fn get_script_count(&self) -> usize {
match self {
TurnChoice::Move(data) => data.get_script_count(),
TurnChoice::Item(data) => data.get_script_count(),
TurnChoice::Switch(data) => data.get_script_count(),
TurnChoice::Flee(data) => data.get_script_count(),
TurnChoice::Pass(data) => data.get_script_count(),
}
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
match self {
TurnChoice::Move(data) => data.get_script_source_data(),
TurnChoice::Item(data) => data.get_script_source_data(),
TurnChoice::Switch(data) => data.get_script_source_data(),
TurnChoice::Flee(data) => data.get_script_source_data(),
TurnChoice::Pass(data) => data.get_script_source_data(),
}
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
match self {
TurnChoice::Move(data) => data.get_own_scripts(scripts),
TurnChoice::Item(data) => data.get_own_scripts(scripts),
TurnChoice::Switch(data) => data.get_own_scripts(scripts),
TurnChoice::Flee(data) => data.get_own_scripts(scripts),
TurnChoice::Pass(data) => data.get_own_scripts(scripts),
}
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
match self {
TurnChoice::Move(data) => data.collect_scripts(scripts),
TurnChoice::Item(data) => data.collect_scripts(scripts),
TurnChoice::Switch(data) => data.collect_scripts(scripts),
TurnChoice::Flee(data) => data.collect_scripts(scripts),
TurnChoice::Pass(data) => data.collect_scripts(scripts),
}
}
}
#[derive(Debug)]
pub struct MoveChoice<'user, 'library> {
used_move: Arc<RwLock<LearnedMove<'library>>>,
target_side: u8,
target_index: u8,
script: ScriptContainer,
priority: i8,
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> MoveChoice<'user, 'library> {
pub fn new(
user: Arc<RwLock<Pokemon<'user, 'library>>>,
used_move: Arc<RwLock<LearnedMove<'library>>>,
target_side: u8,
target_index: u8,
) -> Self {
Self {
used_move,
target_side,
target_index,
script: Default::default(),
priority: 0,
choice_data: Box::new(CommonChoiceData {
user,
speed: 0,
random_value: 0,
has_failed: false,
script_source_data: Default::default(),
}),
}
}
pub fn used_move(&self) -> &Arc<RwLock<LearnedMove<'library>>> {
&self.used_move
}
pub fn target_side(&self) -> u8 {
self.target_side
}
pub fn target_index(&self) -> u8 {
self.target_index
}
pub fn priority(&self) -> i8 {
self.priority
}
pub fn user(&self) -> &Arc<RwLock<Pokemon<'user, 'library>>> {
&self.choice_data.user
}
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> {
fn get_script_count(&self) -> usize {
1 + self.choice_data.user.read().get_script_count()
}
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 collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.get_own_scripts(scripts);
self.choice_data.user.read().collect_scripts(scripts);
}
}
#[derive(Debug)]
pub struct ItemChoice<'user, 'library> {
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> ScriptSource<'user> for ItemChoice<'user, 'library> {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.read().collect_scripts(scripts);
}
}
#[derive(Debug)]
pub struct SwitchChoice<'user, 'library> {
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> ScriptSource<'user> for SwitchChoice<'user, 'library> {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.read().collect_scripts(scripts);
}
}
#[derive(Debug)]
pub struct FleeChoice<'user, 'library> {
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> ScriptSource<'user> for FleeChoice<'user, 'library> {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.read().collect_scripts(scripts);
}
}
#[derive(Debug)]
pub struct PassChoice<'user, 'library> {
choice_data: Box<CommonChoiceData<'user, 'library>>,
}
impl<'user, 'library> ScriptSource<'user> for PassChoice<'user, 'library> {
fn get_script_count(&self) -> usize {
0
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.choice_data.script_source_data
}
fn get_own_scripts(&self, _scripts: &mut Vec<ScriptWrapper>) {}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.choice_data.user.read().collect_scripts(scripts);
}
}
impl<'user, 'library> PartialEq<Self> for TurnChoice<'user, 'library> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
impl<'user, 'library> Eq for TurnChoice<'user, 'library> {}
impl<'user, 'library> PartialOrd for TurnChoice<'user, 'library> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'user, 'library> Ord for TurnChoice<'user, 'library> {
fn cmp(&self, other: &Self) -> Ordering {
match self {
TurnChoice::Move(data) => {
if let TurnChoice::Move(other_data) = other {
let priority_compare = data.priority.cmp(&other_data.priority);
if priority_compare != Ordering::Equal {
return priority_compare;
}
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
Ordering::Greater
}
TurnChoice::Item { .. } => {
if let TurnChoice::Move { .. } = other {
return Ordering::Less;
}
if let TurnChoice::Item { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
Ordering::Greater
}
TurnChoice::Switch { .. } => {
if let TurnChoice::Move { .. } = other {
return Ordering::Less;
}
if let TurnChoice::Item { .. } = other {
return Ordering::Less;
}
if let TurnChoice::Switch { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
Ordering::Greater
}
TurnChoice::Flee { .. } => {
if let TurnChoice::Flee { .. } = other {
let speed_compare = self.speed().cmp(&other.speed());
if speed_compare != Ordering::Equal {
return speed_compare;
}
return self.random_value().cmp(&other.random_value());
}
Ordering::Less
}
TurnChoice::Pass(..) => Ordering::Less,
}
}
}

View File

@ -1,4 +1,5 @@
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;
@ -9,12 +10,12 @@ pub struct EventHook {
evt_hook_function: Vec<fn(&Box<&Event>)>,
}
impl<'a> EventHook {
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<'a, 'b>) {
pub fn trigger<'b>(&self, evt: Event<'b, 'battle, 'library>) {
let b = Box::new(&evt);
for f in &self.evt_hook_function {
f(&b);
@ -29,11 +30,11 @@ impl Debug for EventHook {
}
#[derive(Debug)]
pub enum Event<'a, 'b> {
pub enum Event<'own, 'battle, 'library> {
Switch {
side_index: u8,
index: u8,
pokemon: Option<&'a Pokemon<'b>>,
pokemon: Option<&'own Pokemon<'battle, 'library>>,
},
Swap {
side_index: u8,
@ -41,21 +42,28 @@ pub enum Event<'a, 'b> {
index_b: u8,
},
SpeciesChange {
pokemon: &'a Pokemon<'b>,
species: &'a Species<'b>,
form: &'a Form<'b>,
pokemon: &'own Pokemon<'battle, 'library>,
species: &'own Species<'library>,
form: &'own Form<'library>,
},
FormChange {
pokemon: &'a Pokemon<'b>,
form: &'a Form<'b>,
pokemon: &'own Pokemon<'battle, 'library>,
form: &'own Form<'library>,
},
Damage {
pokemon: &'a Pokemon<'b>,
pokemon: &'own Pokemon<'battle, 'library>,
source: DamageSource,
original_health: u32,
new_health: u32,
},
Faint {
pokemon: &'a Pokemon<'b>,
pokemon: &'own Pokemon<'battle, 'library>,
},
MoveUse {
executing_move: &'own ExecutingMove<'own, 'battle, 'library>,
},
Miss {
user: &'own Pokemon<'battle, 'library>,
},
EndTurn,
}

View File

@ -1,24 +1,26 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::models::pokemon::Pokemon;
use parking_lot::RwLock;
use std::sync::Arc;
#[derive(Debug)]
pub struct ChoiceQueue {
queue: Vec<Arc<ChoiceQueue>>,
pub struct ChoiceQueue<'battle, 'library> {
queue: Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>>,
current: usize,
}
impl ChoiceQueue {
pub fn new(queue: Vec<Arc<ChoiceQueue>>) -> Self {
impl<'battle, 'library> ChoiceQueue<'battle, 'library> {
pub fn new(queue: Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>>) -> Self {
Self { queue, current: 0 }
}
pub fn dequeue(&mut self) -> &Arc<ChoiceQueue> {
pub fn dequeue<'b>(&'b mut self) -> &'b Arc<RwLock<TurnChoice<'battle, 'library>>> {
let c = &self.queue[self.current];
self.current += 1;
c
}
pub fn peek(&mut self) -> &Arc<ChoiceQueue> {
pub fn peek(&mut self) -> &'battle Arc<RwLock<TurnChoice>> {
&self.queue[self.current]
}
@ -29,4 +31,8 @@ impl ChoiceQueue {
pub fn move_pokemon_choice_next(&mut self, _pokemon: &Pokemon) {
todo!()
}
pub(crate) fn get_queue(&self) -> &Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>> {
&self.queue
}
}

View File

@ -1,2 +1,3 @@
pub mod choice_queue;
pub mod target_resolver;
pub mod turn_runner;

View File

@ -5,11 +5,10 @@ use num_traits::abs;
use parking_lot::RwLock;
use std::sync::Arc;
pub type TargetList<'a> = Vec<Option<Arc<RwLock<Pokemon<'a>>>>>;
pub type TargetList<'own, 'library> = Vec<Option<Arc<RwLock<Pokemon<'own, 'library>>>>>;
fn get_all_targets<'b>(battle: &Battle<'b>) -> TargetList<'b> {
let mut v =
Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize);
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() {
for pokemon in side.pokemon() {
v.push(pokemon.as_ref().cloned());
@ -25,16 +24,13 @@ fn get_opposite_side(side: u8) -> u8 {
0
}
fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> {
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() {
return vec![
battle.get_pokemon(side, index).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), index)
.as_ref()
.cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
];
}
if left >= 0 {
@ -43,19 +39,13 @@ fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, left as u8).as_ref().cloned(),
battle.get_pokemon(side, right).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), index)
.as_ref()
.cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
]
} else {
vec![
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, left as u8).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), index)
.as_ref()
.cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
]
};
}
@ -63,14 +53,15 @@ fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<
vec![
battle.get_pokemon(side, index).as_ref().cloned(),
battle.get_pokemon(side, right).as_ref().cloned(),
battle
.get_pokemon(get_opposite_side(side), index)
.as_ref()
.cloned(),
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
]
}
fn get_all_adjacent_opponent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> {
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() {
@ -95,12 +86,12 @@ fn get_all_adjacent_opponent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> Ta
]
}
pub fn resolve_targets<'b>(
pub fn resolve_targets<'b, 'library>(
side: u8,
index: u8,
target: MoveTarget,
battle: &Battle<'b>,
) -> TargetList<'b> {
battle: &Battle<'b, 'library>,
) -> TargetList<'b, 'library> {
match target {
MoveTarget::Adjacent
| MoveTarget::AdjacentAlly

View File

@ -0,0 +1,344 @@
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, script_hook_on_lock, PkmnResult};
use parking_lot::RwLock;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
impl<'own, 'library> Battle<'own, 'library> {
pub(crate) fn run_turn(&mut self) -> PkmnResult<()> {
let cq = self.current_turn_queue();
let mut choice_queue = cq.as_ref().unwrap().write();
// We are now at the very beginning of a turn. We have assigned speeds and priorities to all
// choices, and put them in the correct order.
// The first thing to do is to run the on_before_turn script hook on every choice. This script hook
// is primarily intended to be used to reset variables on a script (for example scripts that need
// to check whether a pokemon was hit this turn. By resetting here, and setting a variable to true
// they can then know this later on.)
for choice in choice_queue.get_queue() {
let choice_guard = choice.read();
let c = choice_guard.deref();
script_hook!(on_before_turn, c, c);
}
// Now we can properly begin executing choices.
// One by one dequeue the turns, and run them. If the battle has ended we do not want to
// continue running however.
while choice_queue.has_next() && !self.has_ended() {
let choice = choice_queue.dequeue().clone();
self.execute_choice(choice.clone())?;
}
// If the battle is not ended, we have arrived at the normal end of a turn. and thus want
// to run the end turn scripts.
// As we want all scripts to run exactly once, including those on the sides and battles,
// we can't just use the default script hook macro on each pokemon. Instead manually call
// the script functions on every script.
if !self.has_ended() {
let mut scripts;
for side in self.sides() {
for pokemon in side.pokemon().iter().flatten() {
scripts = Vec::new();
pokemon.read().get_own_scripts(&mut scripts);
run_scripts!(on_end_turn, scripts,);
}
scripts = Vec::new();
side.get_own_scripts(&mut scripts);
run_scripts!(on_end_turn, scripts,);
}
scripts = Vec::new();
self.get_own_scripts(&mut scripts);
run_scripts!(on_end_turn, scripts,);
}
Ok(())
}
fn execute_choice(&self, choice: Arc<RwLock<TurnChoice<'own, 'library>>>) -> PkmnResult<()> {
let choice_guard = choice.read();
if let TurnChoice::Pass(..) = choice_guard.deref() {
return Ok(());
}
if self.has_ended() {
return Ok(());
}
let user = choice_guard.user().read();
if !user.is_usable() {
return Ok(());
}
if !user.is_on_battlefield() {
return Ok(());
}
if !self.can_use(choice_guard.deref()) {
return Ok(());
}
match choice_guard.deref() {
TurnChoice::Move(..) => self.execute_move_choice(&choice)?,
TurnChoice::Item(_) => {}
TurnChoice::Switch(_) => {}
TurnChoice::Flee(_) => {}
TurnChoice::Pass(_) => {}
}
Ok(())
}
fn execute_move_choice<'func>(
&'func self,
choice: &'func Arc<RwLock<TurnChoice<'own, 'library>>>,
) -> PkmnResult<()> {
let mut write_guard = choice.write();
let choice = write_guard.get_move_turn_data();
let used_move = choice.used_move();
let move_data_lock = used_move.read();
let mut move_data = move_data_lock.move_data();
let mut move_name = move_data.name().clone();
script_hook!(change_move, choice, choice, &mut move_name);
if move_name != *move_data.name() {
move_data = self.library().static_data().moves().get(&move_name).unwrap();
// FIXME: also change the script on the choice.
}
let target_type = move_data.target();
let targets = resolve_targets(choice.target_side(), choice.target_index(), target_type, self);
let mut number_of_hits: u8 = 1;
script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits);
if number_of_hits == 0 {
return Ok(());
}
let mut executing_move = ExecutingMove::new(
&targets,
number_of_hits,
choice.user().clone(),
used_move.clone(),
move_data,
choice.script().clone(),
);
let mut prevented = false;
script_hook!(prevent_move, executing_move, &executing_move, &mut prevented);
if prevented {
return Ok(());
}
if !executing_move.chosen_move().write().try_use(1) {
return Ok(());
}
self.event_hook().trigger(Event::MoveUse {
executing_move: &executing_move,
});
let mut fail = false;
script_hook!(fail_move, executing_move, &executing_move, &mut fail);
if fail {
// TODO: Add fail handling
return Ok(());
}
let mut stop = false;
script_hook!(stop_before_move, executing_move, &executing_move, &mut stop);
if stop {
return Ok(());
}
script_hook!(on_before_move, executing_move, &executing_move);
for target in targets.iter().flatten() {
self.handle_move_for_target(&mut executing_move, target)?;
}
Ok(())
}
fn handle_move_for_target(
&self,
executing_move: &mut ExecutingMove<'_, 'own, '_>,
target: &Arc<RwLock<Pokemon<'own, '_>>>,
) -> PkmnResult<()> {
{
let mut fail = false;
script_hook_on_lock!(fail_incoming_move, target, executing_move, target, &mut fail);
if fail {
// TODO: Add fail handling
return Ok(());
}
}
{
let mut invulnerable = false;
script_hook_on_lock!(is_invulnerable, target, executing_move, target, &mut invulnerable);
if invulnerable {
// TODO: Add fail handling
return Ok(());
}
}
let number_of_hits = executing_move.number_of_hits();
if number_of_hits == 0 {
script_hook_on_lock!(on_move_miss, target, executing_move, target);
self.event_hook().trigger(Event::Miss {
user: executing_move.user().read().deref(),
});
return Ok(());
}
let target_hit_stat = executing_move.get_index_of_target(target)?;
for hit_index in 0..number_of_hits {
if self.has_ended() {
return Ok(());
}
if executing_move.user().read().is_fainted() {
break;
}
if target.read().is_fainted() {
break;
}
{
let mut hit_type = executing_move.use_move().move_type();
script_hook!(
change_move_type,
executing_move,
executing_move,
target,
hit_index,
&mut hit_type
);
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_move_type(hit_type);
let mut effectiveness = self
.library()
.static_data()
.types()
.get_effectiveness(hit_type, target.read().types());
script_hook!(
change_effectiveness,
executing_move,
executing_move,
target,
hit_index,
&mut effectiveness
);
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_effectiveness(effectiveness);
let mut block_critical = false;
script_hook!(
block_critical,
executing_move,
executing_move,
target,
hit_index,
&mut block_critical
);
script_hook_on_lock!(
block_incoming_critical,
target,
executing_move,
target,
hit_index,
&mut block_critical
);
if !block_critical {
let is_critical =
self.library()
.misc_library()
.is_critical(self, executing_move, target, hit_index);
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_critical(is_critical);
}
let base_power = self.library().damage_calculator().get_base_power(
executing_move,
target,
hit_index,
executing_move.get_hit_data(target, hit_index)?,
);
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_base_power(base_power);
let damage = self.library().damage_calculator().get_damage(
executing_move,
target,
hit_index,
executing_move.get_hit_data(target, hit_index)?,
);
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_damage(damage);
if executing_move.use_move().category() == MoveCategory::Status {
if executing_move.use_move().has_secondary_effect() {
let secondary_effect_chance = executing_move.use_move().secondary_effect().chance();
if secondary_effect_chance == -1.0
|| self
.random()
.effect_chance(secondary_effect_chance, executing_move, target, hit_index)
{
script_hook!(on_secondary_effect, executing_move, executing_move, target, hit_index);
// TODO: on fail
}
}
} else {
let mut damage = executing_move
.get_hit_from_raw_index(target_hit_stat + hit_index as usize)
.damage();
let current_health = target.read().current_health();
if damage > current_health {
damage = current_health;
executing_move
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
.set_damage(damage);
}
if damage > 0 {
target.write().damage(damage, DamageSource::AttackDamage);
if !target.read().is_fainted() {
script_hook_on_lock!(on_incoming_hit, target, executing_move, target, hit_index);
} else {
script_hook!(on_opponent_faints, executing_move, executing_move, target, hit_index);
}
if executing_move.use_move().has_secondary_effect() && !target.read().is_fainted() {
let mut prevent_secondary = false;
script_hook_on_lock!(
prevent_secondary_effect,
target,
executing_move,
target,
hit_index,
&mut prevent_secondary
);
if !prevent_secondary {
let secondary_effect_chance = executing_move.use_move().secondary_effect().chance();
if secondary_effect_chance == -1.0
|| self.random().effect_chance(
secondary_effect_chance,
executing_move,
target,
hit_index,
)
{
script_hook!(
on_secondary_effect,
executing_move,
executing_move,
target,
hit_index
);
// TODO: on fail
}
}
}
}
}
}
}
if !executing_move.user().read().is_fainted() {
script_hook!(on_after_hits, executing_move, executing_move, target);
}
Ok(())
}
}

View File

@ -37,8 +37,7 @@ impl BattleStatCalculator {
}
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
(self.calculate_flat_stat(pokemon, stat) as f32 * self.get_stat_boost_modifier(pokemon, stat)) as u32
}
fn calculate_health_stat(&self, pokemon: &Pokemon) -> u32 {

View File

@ -0,0 +1,275 @@
use crate::dynamic_data::models::executing_move::{ExecutingMove, HitData};
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::static_data::{MoveCategory, Statistic};
use crate::{script_hook, script_hook_on_lock};
use parking_lot::RwLock;
use std::sync::Arc;
pub trait DamageLibrary: std::fmt::Debug {
fn has_randomness(&self) -> bool;
fn get_damage(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> u32;
fn get_base_power(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> u8;
fn get_stat_modifier(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> f32;
fn get_damage_modifier(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> f32;
}
#[derive(Debug)]
pub struct Gen7DamageLibrary {
has_randomness: bool,
}
impl Gen7DamageLibrary {
pub fn new(has_randomness: bool) -> Self {
Self { has_randomness }
}
}
impl DamageLibrary for Gen7DamageLibrary {
fn has_randomness(&self) -> bool {
self.has_randomness
}
fn get_damage(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> u32 {
if executing_move.use_move().category() == MoveCategory::Status {
return 0;
}
let level_modifier = ((2.0 * executing_move.user().read().level() as f32) / 5.0).floor() + 2.0;
let base_power = hit_data.base_power();
let stat_modifier = self.get_stat_modifier(executing_move, target, hit_number, hit_data);
let damage_modifier = self.get_damage_modifier(executing_move, target, hit_number, hit_data);
let mut float_damage = (level_modifier * base_power as f32).floor();
float_damage = (float_damage * stat_modifier).floor();
float_damage = (float_damage / 50.0).floor() + 2.0;
float_damage = (float_damage * damage_modifier).floor();
if executing_move.target_count() > 1 {
float_damage = (float_damage * 0.75).floor();
}
if hit_data.is_critical() {
let mut crit_modifier = 1.5;
script_hook!(
change_critical_modifier,
executing_move,
executing_move,
target,
hit_number,
&mut crit_modifier
);
float_damage = (float_damage * crit_modifier).floor();
}
if self.has_randomness {
let battle = executing_move.user().read().get_battle().unwrap().upgrade().unwrap();
let random_percentage = 85 + battle.read().random().get_between(0, 16);
float_damage = (float_damage * (random_percentage as f32 / 100.0)).floor();
}
if executing_move.user().read().types().contains(&hit_data.move_type()) {
let mut stab_modifier = 1.5;
script_hook!(
change_stab_modifier,
executing_move,
executing_move,
target,
hit_number,
&mut stab_modifier
);
float_damage = (float_damage * stab_modifier).floor();
}
float_damage = (float_damage * hit_data.effectiveness()).floor();
let mut damage = if float_damage <= 0.0 {
if hit_data.effectiveness() == 0.0 {
0
} else {
1
}
} else if float_damage >= u32::MAX as f32 {
u32::MAX
} else {
float_damage as u32
};
script_hook!(
change_damage,
executing_move,
executing_move,
target,
hit_number,
&mut damage
);
script_hook_on_lock!(
change_incoming_damage,
target,
executing_move,
target,
hit_number,
&mut damage
);
damage
}
fn get_base_power(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
_hit_data: &HitData,
) -> u8 {
if executing_move.use_move().category() == MoveCategory::Status {
return 0;
}
let mut base_power = executing_move.use_move().base_power();
script_hook!(
change_base_power,
executing_move,
executing_move,
target,
hit_number,
&mut base_power
);
base_power
}
fn get_stat_modifier(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
hit_data: &HitData,
) -> f32 {
let mut user = executing_move.user().clone();
script_hook!(
change_damage_stats_user,
executing_move,
executing_move,
target,
hit_number,
&mut user
);
let offensive_stat;
let defensive_stat;
if executing_move.use_move().category() == MoveCategory::Physical {
offensive_stat = Statistic::Attack;
defensive_stat = Statistic::Defense;
} else {
offensive_stat = Statistic::SpecialAttack;
defensive_stat = Statistic::SpecialDefense;
}
let mut bypass_defensive_stat_boost =
hit_data.is_critical() && target.read().stat_boost().get_stat(defensive_stat) > 0;
script_hook!(
bypass_defensive_stat_boost,
executing_move,
executing_move,
target,
hit_number,
&mut bypass_defensive_stat_boost
);
let mut bypass_offensive_stat_boost =
hit_data.is_critical() && user.read().stat_boost().get_stat(offensive_stat) > 0;
script_hook!(
bypass_offensive_stat_boost,
executing_move,
executing_move,
target,
hit_number,
&mut bypass_offensive_stat_boost
);
let mut defensive_stat = if bypass_defensive_stat_boost {
target.read().flat_stats().get_stat(offensive_stat)
} else {
target.read().boosted_stats().get_stat(offensive_stat)
};
let mut offensive_stat = if bypass_offensive_stat_boost {
user.read().flat_stats().get_stat(offensive_stat)
} else {
user.read().boosted_stats().get_stat(offensive_stat)
};
script_hook!(
change_defensive_stat_value,
executing_move,
executing_move,
target,
hit_number,
&mut defensive_stat
);
script_hook!(
change_offensive_stat_value,
executing_move,
executing_move,
target,
hit_number,
&mut offensive_stat
);
let mut stat_modifier = offensive_stat as f32 / defensive_stat as f32;
script_hook!(
change_damage_stat_modifier,
executing_move,
executing_move,
target,
hit_number,
&mut stat_modifier
);
stat_modifier
}
fn get_damage_modifier(
&self,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
_hit_data: &HitData,
) -> f32 {
let mut modifier = 1.0;
script_hook!(
change_damage_modifier,
executing_move,
executing_move,
target,
hit_number,
&mut modifier
);
modifier
}
}

View File

@ -1,4 +1,6 @@
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;
@ -7,27 +9,30 @@ use crate::static_data::libraries::static_data::StaticData;
use crate::{PkmnResult, StringKey};
#[derive(Debug)]
pub struct DynamicLibrary<'a> {
static_data: StaticData<'a>,
pub struct DynamicLibrary {
static_data: StaticData<'static>,
stat_calculator: BattleStatCalculator,
damage_calculator: Box<dyn DamageLibrary>,
misc_library: Box<dyn MiscLibrary<'static>>,
}
impl<'a> DynamicLibrary<'a> {
pub fn static_data(&self) -> &StaticData<'a> {
impl<'library> DynamicLibrary {
pub fn static_data(&self) -> &StaticData<'library> {
&self.static_data
}
pub fn stat_calculator(&self) -> &BattleStatCalculator {
&self.stat_calculator
}
pub fn load_script(
&self,
_category: ScriptCategory,
_key: &StringKey,
) -> PkmnResult<Option<Box<dyn Script>>> {
todo!()
pub fn damage_calculator(&self) -> &Box<dyn DamageLibrary> {
&self.damage_calculator
}
pub fn misc_library(&self) -> &Box<dyn MiscLibrary<'static>> {
&self.misc_library
}
pub fn load_script(&self, _category: ScriptCategory, _key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> {
todo!()
}
pub fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Box<dyn ItemScript>>> {
todo!()
}
@ -36,13 +41,17 @@ impl<'a> DynamicLibrary<'a> {
#[cfg(test)]
pub mod test {
use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
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<'a>() -> DynamicLibrary<'a> {
pub fn build<'library>() -> DynamicLibrary {
DynamicLibrary {
static_data: static_data::test::build(),
stat_calculator: BattleStatCalculator {},
damage_calculator: Box::new(Gen7DamageLibrary::new(false)),
misc_library: Box::new(Gen7MiscLibrary::new()),
}
}
}

View File

@ -0,0 +1,126 @@
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 parking_lot::RwLock;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
pub trait MiscLibrary<'library> {
fn is_critical(
&self,
battle: &Battle,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
) -> bool;
fn can_flee(&self, choice: &SwitchChoice) -> bool;
fn replacement_move<'func>(
&'func self,
user: &Arc<RwLock<Pokemon<'func, 'library>>>,
target_side: u8,
target_index: u8,
) -> TurnChoice<'func, 'library>;
// TODO: can evolve from level up?
// 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,
struggle_learned_move: Arc<RwLock<LearnedMove<'library>>>,
}
impl<'library> Gen7MiscLibrary<'library> {
pub fn new() -> Self {
let struggle_data = Box::new(MoveData::new(
&StringKey::new("struggle"),
0,
MoveCategory::Physical,
50,
255,
10,
MoveTarget::Any,
0,
SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![]),
HashSet::new(),
));
let struggle_ptr = Box::into_raw(struggle_data);
let struggle_learned_move = Arc::new(RwLock::new(LearnedMove::new(
unsafe { &*struggle_ptr },
MoveLearnMethod::Unknown,
)));
Self {
struggle_data: struggle_ptr,
struggle_learned_move,
}
}
}
impl<'library> Drop for Gen7MiscLibrary<'library> {
fn drop(&mut self) {
unsafe {
Box::from_raw(self.struggle_data as *mut MoveData);
}
}
}
impl<'library> MiscLibrary<'library> for Gen7MiscLibrary<'library> {
fn is_critical(
&self,
battle: &Battle,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
) -> bool {
if executing_move.use_move().category() == MoveCategory::Status {
return false;
}
let mut crit_stage = 0;
script_hook!(
change_critical_stage,
executing_move,
executing_move,
target,
hit_number,
&mut crit_stage
);
// Crit stage is an unsigned byte, so we only care about values of 0 or higher.
// For a critical stage of 3+ we always return true.
match crit_stage {
0 => battle.random().get_max(24) == 0,
1 => battle.random().get_max(8) == 0,
2 => battle.random().get_max(2) == 0,
_ => true,
}
}
fn can_flee(&self, _choice: &SwitchChoice) -> bool {
todo!()
}
fn replacement_move<'func>(
&'func self,
user: &Arc<RwLock<Pokemon<'func, 'library>>>,
target_side: u8,
target_index: u8,
) -> TurnChoice<'func, 'library> {
TurnChoice::Move(MoveChoice::new(
user.clone(),
self.struggle_learned_move.clone(),
target_side,
target_index,
))
}
}

View File

@ -1,3 +1,5 @@
pub mod battle_stat_calculator;
pub mod damage_library;
pub mod dynamic_library;
pub mod script_resolver;
pub mod misc_library;

View File

@ -1,5 +1,5 @@
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::event_hook::EventHook;
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;
@ -13,35 +13,36 @@ 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::{PkmnResult, ScriptCategory, StringKey};
use crate::{script_hook, PkmnResult, ScriptCategory, StringKey};
use parking_lot::RwLock;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
#[derive(Debug)]
pub struct Battle<'a> {
library: &'a DynamicLibrary<'a>,
parties: Vec<BattleParty<'a>>,
pub struct Battle<'own, 'library> {
library: &'own DynamicLibrary,
parties: Vec<BattleParty<'own, 'library>>,
can_flee: bool,
number_of_sides: u8,
pokemon_per_side: u8,
sides: Vec<BattleSide<'a>>,
sides: Vec<BattleSide<'own, 'library>>,
random: BattleRandom,
current_turn_queue: Option<ChoiceQueue>,
current_turn_queue: Option<Arc<RwLock<ChoiceQueue<'own, 'library>>>>,
has_ended: bool,
result: BattleResult,
event_hook: EventHook,
history_holder: Box<HistoryHolder>,
current_turn: u32,
volatile_scripts: Arc<RwLock<ScriptSet>>,
last_turn_time: i64,
last_turn_time: chrono::Duration,
script_source_data: RwLock<ScriptSourceData>,
}
impl<'a> Battle<'a> {
impl<'own, 'library> Battle<'own, 'library> {
pub fn new(
library: &'a DynamicLibrary<'a>,
parties: Vec<BattleParty<'a>>,
library: &'own DynamicLibrary,
parties: Vec<BattleParty<'own, 'library>>,
can_flee: bool,
number_of_sides: u8,
pokemon_per_side: u8,
@ -68,21 +69,20 @@ impl<'a> Battle<'a> {
history_holder: Box::new(HistoryHolder {}),
current_turn: 0,
volatile_scripts: Default::default(),
last_turn_time: 0,
last_turn_time: chrono::Duration::zero(),
script_source_data: Default::default(),
}));
for i in 0..number_of_sides {
battle.write().sides[i as usize] =
BattleSide::new(i, Arc::downgrade(&battle), pokemon_per_side);
battle.write().sides[i as usize] = BattleSide::new(i, Arc::downgrade(&battle), pokemon_per_side);
}
battle
}
pub fn library(&self) -> &'a DynamicLibrary<'a> {
pub fn library(&self) -> &'own DynamicLibrary {
self.library
}
pub fn parties(&self) -> &Vec<BattleParty<'a>> {
pub fn parties(&self) -> &Vec<BattleParty<'own, 'library>> {
&self.parties
}
pub fn can_flee(&self) -> bool {
@ -94,10 +94,10 @@ impl<'a> Battle<'a> {
pub fn pokemon_per_side(&self) -> u8 {
self.pokemon_per_side
}
pub fn sides(&self) -> &Vec<BattleSide<'a>> {
pub fn sides(&self) -> &Vec<BattleSide<'own, 'library>> {
&self.sides
}
pub fn sides_mut(&mut self) -> &mut Vec<BattleSide<'a>> {
pub fn sides_mut(&mut self) -> &mut Vec<BattleSide<'own, 'library>> {
&mut self.sides
}
@ -119,14 +119,14 @@ impl<'a> Battle<'a> {
pub fn current_turn(&self) -> u32 {
self.current_turn
}
pub fn last_turn_time(&self) -> i64 {
pub fn last_turn_time(&self) -> chrono::Duration {
self.last_turn_time
}
pub fn current_turn_queue(&self) -> &Option<ChoiceQueue> {
pub fn current_turn_queue(&self) -> &Option<Arc<RwLock<ChoiceQueue<'own, 'library>>>> {
&self.current_turn_queue
}
pub fn get_pokemon(&self, side: u8, index: u8) -> &Option<Arc<RwLock<Pokemon<'a>>>> {
pub fn get_pokemon(&self, side: u8, index: u8) -> &Option<Arc<RwLock<Pokemon<'own, 'library>>>> {
let side = self.sides.get(side as usize);
if side.is_none() {
return &None;
@ -183,34 +183,99 @@ impl<'a> Battle<'a> {
pub fn can_use(&self, choice: &TurnChoice) -> bool {
// If the user is not usable, we obviously can;t use the choice.
if !choice.user().is_usable() {
if !choice.user().read().is_usable() {
return false;
}
if let TurnChoice::Move {
used_move,
target_side,
target_index,
user,
} = choice
{
if let TurnChoice::Move(data) = choice {
// TODO: Hook to change number of PP needed.
if used_move.remaining_pp() < 1 {
if data.used_move().read().remaining_pp() < 1 {
return false;
}
if !is_valid_target(
*target_side,
*target_index,
used_move.move_data().target(),
user,
data.target_side(),
data.target_index(),
data.used_move().read().move_data().target(),
choice.user().read().deref(),
) {
return false;
}
}
true
}
pub fn try_set_choice(&mut self, choice: TurnChoice<'own, 'library>) -> PkmnResult<bool> {
if !self.can_use(&choice) {
return Ok(false);
}
if !choice.user().read().is_on_battlefield() {
return Ok(false);
}
let side = choice.user().read().get_battle_side_index();
if side.is_none() {
return Ok(false);
}
self.sides[side.unwrap() as usize].set_choice(choice);
self.check_choices_set_and_run()?;
Ok(true)
}
fn check_choices_set_and_run(&mut self) -> PkmnResult<()> {
for side in &self.sides {
if !side.all_choices_set() {
return Ok(());
}
if !side.all_slots_filled() {
return Ok(());
}
}
let start_time = chrono::Utc::now();
let mut choices = Vec::with_capacity(self.number_of_sides as usize * self.pokemon_per_side as usize);
for side in &mut self.sides {
for choice in side.choices() {
if choice.is_none() {
panic!("Choice was none, but all choices were set? Logic error.");
}
let mut choice_guard = choice.as_ref().unwrap().write();
let c = choice_guard.deref();
if let TurnChoice::Move(data) = c {
let mut change_priority = data.priority();
script_hook!(change_priority, c, c, &mut change_priority);
if change_priority != data.priority() {
if let TurnChoice::Move(data) = choice_guard.deref_mut() {
*data.priority_mut() = change_priority;
}
}
}
let mut speed = choice_guard.speed();
let c = choice_guard.deref();
script_hook!(change_speed, c, c, &mut speed);
*choice_guard.speed_mut() = speed;
choice_guard.set_random_value(self.random.get() as u32);
choices.push(choice.as_ref().unwrap().clone());
}
side.reset_choices();
}
self.current_turn += 1;
choices.sort_unstable_by(|a, b| b.read().deref().cmp(a.read().deref()));
self.current_turn_queue = Some(Arc::new(RwLock::new(ChoiceQueue::new(choices))));
{
self.run_turn()?;
}
self.current_turn_queue = None;
self.event_hook.trigger(Event::EndTurn);
let end_time = chrono::Utc::now();
let time = end_time - start_time;
self.last_turn_time = time;
Ok(())
}
}
impl<'a> VolatileScripts<'a> for Battle<'a> {
impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<RwLock<ScriptSet>> {
&self.volatile_scripts
}
@ -220,7 +285,7 @@ impl<'a> VolatileScripts<'a> for Battle<'a> {
}
}
impl<'a> ScriptSource<'a> for Battle<'a> {
impl<'own, 'library> ScriptSource<'own> for Battle<'own, 'library> {
fn get_script_count(&self) -> usize {
1
}

View File

@ -1,12 +1,12 @@
use crate::dynamic_data::models::pokemon_party::PokemonParty;
#[derive(Debug)]
pub struct BattleParty<'a> {
party: &'a PokemonParty<'a>,
pub struct BattleParty<'own, 'library> {
party: &'own PokemonParty<'own, 'library>,
responsible_indices: Vec<(u8, u8)>,
}
impl<'a> BattleParty<'a> {
impl<'own, 'library> BattleParty<'own, 'library> {
pub fn is_responsible_for_index(&self, side: u8, index: u8) -> bool {
for responsible_index in &self.responsible_indices {
if responsible_index.0 == side && responsible_index.1 == index {

View File

@ -1,6 +1,11 @@
use crate::dynamic_data::models::executing_move::ExecutingMove;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::dynamic_data::script_handling::ScriptSource;
use crate::utils::random::Random;
use crate::{script_hook, script_hook_on_lock};
use parking_lot::RwLock;
use std::fmt::{Debug, Formatter};
use std::sync::Mutex;
use std::sync::{Arc, Mutex};
#[derive(Default)]
pub struct BattleRandom {
@ -27,6 +32,32 @@ impl BattleRandom {
pub fn get_between(&self, min: i32, max: i32) -> i32 {
return self.get_rng().lock().unwrap().get_between(min, max);
}
pub fn effect_chance(
&self,
mut chance: f32,
executing_move: &ExecutingMove,
target: &Arc<RwLock<Pokemon>>,
hit_number: u8,
) -> bool {
script_hook!(
change_effect_chance,
executing_move,
executing_move,
target,
hit_number,
&mut chance
);
script_hook_on_lock!(
change_incoming_effect_chance,
target,
executing_move,
target,
hit_number,
&mut chance
);
self.get_rng().lock().unwrap().get_float() < (chance / 100.0)
}
}
impl Debug for BattleRandom {

View File

@ -13,22 +13,22 @@ use std::ops::Deref;
use std::sync::{Arc, Weak};
#[derive(Debug)]
pub struct BattleSide<'a> {
pub struct BattleSide<'own, 'library> {
index: u8,
pokemon_per_side: u8,
pokemon: Vec<Option<Arc<RwLock<Pokemon<'a>>>>>,
choices: Vec<Option<Arc<TurnChoice<'a>>>>,
pokemon: Vec<Option<Arc<RwLock<Pokemon<'own, 'library>>>>>,
choices: Vec<Option<Arc<RwLock<TurnChoice<'own, 'library>>>>>,
fillable_slots: Vec<bool>,
choices_set: u8,
battle: Weak<RwLock<Battle<'a>>>,
battle: Weak<RwLock<Battle<'own, 'library>>>,
has_fled_battle: bool,
volatile_scripts: Arc<RwLock<ScriptSet>>,
script_source_data: RwLock<ScriptSourceData>,
}
impl<'a> BattleSide<'a> {
pub fn new(index: u8, battle: Weak<RwLock<Battle<'a>>>, pokemon_per_side: u8) -> Self {
impl<'own, 'library> BattleSide<'own, 'library> {
pub fn new(index: u8, battle: Weak<RwLock<Battle<'own, 'library>>>, pokemon_per_side: u8) -> Self {
let mut pokemon = Vec::with_capacity(pokemon_per_side as usize);
let mut choices = Vec::with_capacity(pokemon_per_side as usize);
let mut fillable_slots = Vec::with_capacity(pokemon_per_side as usize);
@ -58,19 +58,23 @@ impl<'a> BattleSide<'a> {
pub fn pokemon_per_side(&self) -> u8 {
self.pokemon_per_side
}
pub fn pokemon(&self) -> &Vec<Option<Arc<RwLock<Pokemon<'a>>>>> {
pub fn pokemon(&self) -> &Vec<Option<Arc<RwLock<Pokemon<'own, 'library>>>>> {
&self.pokemon
}
pub fn choices(&self) -> &Vec<Option<Arc<TurnChoice<'a>>>> {
pub fn choices(&self) -> &Vec<Option<Arc<RwLock<TurnChoice<'own, 'library>>>>> {
&self.choices
}
pub fn choices_mut(&mut self) -> &mut Vec<Option<Arc<RwLock<TurnChoice<'own, 'library>>>>> {
&mut self.choices
}
pub fn fillable_slots(&self) -> &Vec<bool> {
&self.fillable_slots
}
pub fn choices_set(&self) -> u8 {
self.choices_set
}
pub fn battle(&self) -> &Weak<RwLock<Battle<'a>>> {
pub fn battle(&self) -> &Weak<RwLock<Battle<'own, 'library>>> {
&self.battle
}
pub fn has_fled_battle(&self) -> bool {
@ -103,11 +107,11 @@ impl<'a> BattleSide<'a> {
true
}
pub fn set_choice(&mut self, choice: TurnChoice<'a>) {
pub fn set_choice(&mut self, choice: TurnChoice<'own, 'library>) {
for (index, pokemon_slot) in self.pokemon.iter().enumerate() {
if let Some(pokemon) = pokemon_slot {
if pokemon.read().unique_identifier() == choice.user().unique_identifier() {
self.choices[index] = Some(Arc::new(choice));
if std::ptr::eq(pokemon.data_ptr(), choice.user().data_ptr()) {
self.choices[index] = Some(Arc::new(RwLock::new(choice)));
self.choices_set += 1;
return;
}
@ -115,11 +119,17 @@ impl<'a> BattleSide<'a> {
}
}
pub fn reset_choices(&mut self) {
for i in 0..self.choices.len() {
self.choices[i] = None;
}
}
pub fn force_clear_pokemon(&mut self, index: u8) {
self.pokemon[index as usize] = None;
}
pub fn set_pokemon(&mut self, index: u8, pokemon: Option<Arc<RwLock<Pokemon<'a>>>>) {
pub fn set_pokemon(&mut self, index: u8, pokemon: Option<Arc<RwLock<Pokemon<'own, 'library>>>>) {
let old = &mut self.pokemon[index as usize];
if let Some(old_pokemon) = old {
let mut p = old_pokemon.write();
@ -163,7 +173,7 @@ impl<'a> BattleSide<'a> {
}
}
pub fn is_pokemon_on_side(&self, pokemon: Arc<Pokemon<'a>>) -> bool {
pub fn is_pokemon_on_side(&self, pokemon: Arc<Pokemon<'own, 'library>>) -> bool {
for p in self.pokemon.iter().flatten() {
if p.read().unique_identifier() == pokemon.unique_identifier() {
return true;
@ -172,7 +182,7 @@ impl<'a> BattleSide<'a> {
false
}
pub fn mark_slot_as_unfillable(&mut self, pokemon: &Pokemon<'a>) {
pub fn mark_slot_as_unfillable(&mut self, pokemon: &Pokemon<'own, 'library>) {
for (i, slot) in self.pokemon.iter().enumerate() {
if let Some(p) = slot {
if p.read().deref() as *const Pokemon == pokemon as *const Pokemon {
@ -183,7 +193,7 @@ impl<'a> BattleSide<'a> {
}
}
pub fn is_slot_unfillable(&self, pokemon: Arc<Pokemon<'a>>) -> bool {
pub fn is_slot_unfillable(&self, pokemon: Arc<Pokemon<'own, 'library>>) -> bool {
for (i, slot) in self.pokemon.iter().enumerate() {
if let Some(p) = slot {
if p.read().unique_identifier() == pokemon.unique_identifier() {
@ -263,7 +273,7 @@ impl<'a> BattleSide<'a> {
}
}
impl<'a> VolatileScripts<'a> for BattleSide<'a> {
impl<'own, 'library> VolatileScripts<'own> for BattleSide<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<RwLock<ScriptSet>> {
&self.volatile_scripts
}
@ -278,7 +288,7 @@ impl<'a> VolatileScripts<'a> for BattleSide<'a> {
}
}
impl<'a> ScriptSource<'a> for BattleSide<'a> {
impl<'own, 'library> ScriptSource<'own> for BattleSide<'own, 'library> {
fn get_script_count(&self) -> usize {
self.battle.upgrade().unwrap().read().get_script_count() + 1
}
@ -293,10 +303,6 @@ impl<'a> ScriptSource<'a> for BattleSide<'a> {
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.get_own_scripts(scripts);
self.battle
.upgrade()
.unwrap()
.read()
.collect_scripts(scripts);
self.battle.upgrade().unwrap().read().collect_scripts(scripts);
}
}

View File

@ -1,3 +1,4 @@
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;
@ -5,9 +6,9 @@ use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, Scrip
use crate::static_data::MoveData;
use crate::{PkmnResult, PokemonError};
use parking_lot::RwLock;
use std::ops::Deref;
use std::sync::Arc;
#[derive(Default)]
#[derive(Default, Debug)]
pub struct HitData {
critical: bool,
base_power: u8,
@ -57,24 +58,25 @@ impl HitData {
}
}
pub struct ExecutingMove<'a, 'b> {
#[derive(Debug)]
pub struct ExecutingMove<'own, 'battle, 'library> {
number_of_hits: u8,
hits: Vec<HitData>,
user: &'a Pokemon<'b>,
chosen_move: &'a LearnedMove<'a>,
use_move: &'a MoveData,
user: Arc<RwLock<Pokemon<'battle, 'library>>>,
chosen_move: Arc<RwLock<LearnedMove<'library>>>,
use_move: &'own MoveData,
script: ScriptContainer,
targets: Vec<Option<&'a Pokemon<'b>>>,
targets: &'own TargetList<'battle, 'library>,
script_source_data: RwLock<ScriptSourceData>,
}
impl<'a, 'b> ExecutingMove<'a, 'b> {
impl<'own, 'battle, 'library> ExecutingMove<'own, 'battle, 'library> {
pub fn new(
targets: Vec<Option<&'a Pokemon<'b>>>,
targets: &'own TargetList<'battle, 'library>,
number_of_hits: u8,
user: &'a Pokemon<'b>,
chosen_move: &'a LearnedMove,
use_move: &'a MoveData,
user: Arc<RwLock<Pokemon<'battle, 'library>>>,
chosen_move: Arc<RwLock<LearnedMove<'library>>>,
use_move: &'own MoveData,
script: ScriptContainer,
) -> Self {
let total_hits = number_of_hits as usize * targets.len();
@ -99,23 +101,28 @@ impl<'a, 'b> ExecutingMove<'a, 'b> {
pub fn number_of_hits(&self) -> u8 {
self.number_of_hits
}
pub fn user(&self) -> &'a Pokemon<'b> {
self.user
pub fn user(&self) -> &Arc<RwLock<Pokemon<'battle, 'library>>> {
&self.user
}
pub fn chosen_move(&self) -> &'a LearnedMove {
self.chosen_move
pub fn chosen_move(&self) -> &Arc<RwLock<LearnedMove<'library>>> {
&self.chosen_move
}
pub fn use_move(&self) -> &'a MoveData {
pub fn use_move(&self) -> &'own MoveData {
self.use_move
}
pub fn script(&self) -> &ScriptContainer {
&self.script
}
pub fn get_hit_data(&self, for_target: &Pokemon<'b>, hit: u8) -> PkmnResult<&HitData> {
pub fn get_hit_data<'func>(
&'func self,
for_target: &'func Arc<RwLock<Pokemon<'battle, 'library>>>,
hit: u8,
) -> PkmnResult<&'func HitData> {
for (index, target) in self.targets.iter().enumerate() {
if let Some(target) = target {
if std::ptr::eq(target.deref(), for_target) {
if std::ptr::eq(target.data_ptr(), for_target.data_ptr()) {
let i = index * self.number_of_hits as usize + hit as usize;
return Ok(&self.hits[i]);
}
@ -124,29 +131,39 @@ impl<'a, 'b> ExecutingMove<'a, 'b> {
Err(PokemonError::InvalidTargetRequested)
}
pub fn get_target_slice(&self, for_target: &Pokemon<'b>) -> PkmnResult<&[HitData]> {
pub fn is_pokemon_target(&self, pokemon: &Arc<RwLock<Pokemon<'battle, 'library>>>) -> bool {
for target in self.targets.iter().flatten() {
if std::ptr::eq(target.data_ptr(), pokemon.data_ptr()) {
return true;
}
}
false
}
pub(crate) fn get_index_of_target(
&self,
for_target: &Arc<RwLock<Pokemon<'battle, 'library>>>,
) -> PkmnResult<usize> {
for (index, target) in self.targets.iter().enumerate() {
if let Some(target) = target {
if std::ptr::eq(target.deref(), for_target) {
if std::ptr::eq(target.data_ptr(), for_target.data_ptr()) {
let i = index * self.number_of_hits as usize;
return Ok(&self.hits[i..i + self.number_of_hits as usize]);
return Ok(i);
}
}
}
Err(PokemonError::InvalidTargetRequested)
}
pub fn is_pokemon_target(&self, pokemon: &Pokemon<'b>) -> bool {
for target in self.targets.iter().flatten() {
if std::ptr::eq(target.deref(), pokemon) {
return true;
}
}
false
pub(crate) fn get_hit_from_raw_index(&self, index: usize) -> &HitData {
&self.hits[index]
}
pub(crate) fn get_hit_from_raw_index_mut(&mut self, index: usize) -> &mut HitData {
&mut self.hits[index]
}
}
impl<'a, 'b> ScriptSource<'a> for ExecutingMove<'a, 'b> {
impl<'own, 'battle, 'library> ScriptSource<'own> for ExecutingMove<'own, 'battle, 'library> {
fn get_script_count(&self) -> usize {
1
}
@ -161,6 +178,6 @@ impl<'a, 'b> ScriptSource<'a> for ExecutingMove<'a, 'b> {
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
self.get_own_scripts(scripts);
self.user.get_own_scripts(scripts);
self.user.read().get_own_scripts(scripts);
}
}

View File

@ -1,8 +1,8 @@
use crate::static_data::MoveData;
#[derive(Debug)]
pub struct LearnedMove<'a> {
move_data: &'a MoveData,
pub struct LearnedMove<'library> {
move_data: &'library MoveData,
max_pp: u8,
remaining_pp: u8,
learn_method: MoveLearnMethod,
@ -37,4 +37,12 @@ impl<'a> LearnedMove<'a> {
pub fn learn_method(&self) -> MoveLearnMethod {
self.learn_method
}
pub fn try_use(&mut self, amount: u8) -> bool {
if amount > self.remaining_pp {
return false;
}
self.remaining_pp -= amount;
return true;
}
}

View File

@ -8,3 +8,4 @@ pub mod executing_move;
pub mod learned_move;
pub mod pokemon;
pub mod pokemon_party;
pub mod pokemon_builder;

View File

@ -23,44 +23,47 @@ use parking_lot::RwLock;
use std::sync::{Arc, Weak};
#[derive(Debug)]
pub struct PokemonBattleData<'a> {
battle: Weak<RwLock<Battle<'a>>>,
pub struct PokemonBattleData<'pokemon, 'library> {
battle: Weak<RwLock<Battle<'pokemon, 'library>>>,
battle_side_index: u8,
index: u8,
on_battle_field: bool,
seen_opponents: Vec<Weak<RwLock<Pokemon<'a>>>>,
seen_opponents: Vec<Weak<RwLock<Pokemon<'pokemon, 'library>>>>,
}
impl<'a> PokemonBattleData<'a> {
pub fn battle(&'a mut self) -> &'a mut Weak<RwLock<Battle<'a>>> {
impl<'pokemon, 'library> PokemonBattleData<'pokemon, 'library> {
pub fn battle(&mut self) -> &mut Weak<RwLock<Battle<'pokemon, 'library>>> {
&mut self.battle
}
pub fn battle_side_index(&self) -> u8 {
self.battle_side_index
}
pub fn on_battle_field(&'a mut self) -> &mut bool {
pub fn on_battle_field(&mut self) -> &mut bool {
&mut self.on_battle_field
}
pub fn seen_opponents(&'a mut self) -> &'a mut Vec<Weak<RwLock<Pokemon<'a>>>> {
pub fn seen_opponents(&mut self) -> &mut Vec<Weak<RwLock<Pokemon<'pokemon, 'library>>>> {
&mut self.seen_opponents
}
}
#[derive(Debug)]
pub struct Pokemon<'a> {
library: &'a DynamicLibrary<'a>,
species: &'a Species<'a>,
form: &'a Form<'a>,
pub struct Pokemon<'own, 'library>
where
'own: 'library,
{
library: &'own DynamicLibrary,
species: &'own Species<'library>,
form: &'own Form<'library>,
display_species: Option<&'a Species<'a>>,
display_form: Option<&'a Form<'a>>,
display_species: Option<&'own Species<'library>>,
display_form: Option<&'own Form<'library>>,
level: LevelInt,
experience: u32,
unique_identifier: u32,
gender: Gender,
coloring: u8,
held_item: Option<&'a Item>,
held_item: Option<&'own Item>,
current_health: u32,
weight: f32,
@ -71,7 +74,7 @@ pub struct Pokemon<'a> {
boosted_stats: StatisticSet<u32>,
individual_values: ClampedStatisticSet<u8, 0, 31>,
effort_values: ClampedStatisticSet<u8, 0, 252>,
nature: &'a Nature,
nature: &'own Nature,
nickname: Option<String>,
@ -79,9 +82,9 @@ pub struct Pokemon<'a> {
is_ability_overridden: bool,
override_ability: Option<Ability>,
battle_data: Option<PokemonBattleData<'a>>,
battle_data: Option<PokemonBattleData<'own, 'library>>,
moves: [Option<LearnedMove<'a>>; MAX_MOVES],
moves: [Option<LearnedMove<'library>>; MAX_MOVES],
allowed_experience: bool,
types: Vec<u8>,
@ -96,18 +99,18 @@ pub struct Pokemon<'a> {
script_source_data: RwLock<ScriptSourceData>,
}
impl<'a> Pokemon<'a> {
impl<'own, 'library> Pokemon<'own, 'library> {
pub fn new(
library: &'a DynamicLibrary,
species: &'a Species,
form: &'a Form,
library: &'own DynamicLibrary,
species: &'own Species,
form: &'own Form,
ability: AbilityIndex,
level: LevelInt,
unique_identifier: u32,
gender: Gender,
coloring: u8,
nature: &StringKey,
) -> Pokemon<'a> {
) -> Self {
// Calculate experience from the level for the specified growth rate.
let experience = library
.static_data()
@ -121,7 +124,7 @@ impl<'a> Pokemon<'a> {
.natures()
.get_nature(&nature)
.expect("Unknown nature name was given.");
let mut pokemon = Pokemon {
let mut pokemon = Self {
library,
species,
form,
@ -162,23 +165,23 @@ impl<'a> Pokemon<'a> {
pokemon
}
pub fn library(&self) -> &'a DynamicLibrary<'a> {
pub fn library(&self) -> &'own DynamicLibrary {
self.library
}
pub fn species(&self) -> &'a Species<'a> {
pub fn species(&self) -> &'own Species<'library> {
self.species
}
pub fn form(&self) -> &'a Form<'a> {
pub fn form(&self) -> &'own Form<'library> {
self.form
}
pub fn display_species(&self) -> &'a Species<'a> {
pub fn display_species(&self) -> &'own Species<'library> {
if let Some(v) = self.display_species {
v
} else {
self.species
}
}
pub fn display_form(&self) -> &'a Form<'a> {
pub fn display_form(&self) -> &'own Form<'library> {
if let Some(v) = self.display_form {
v
} else {
@ -201,7 +204,7 @@ impl<'a> Pokemon<'a> {
pub fn coloring(&self) -> u8 {
self.coloring
}
pub fn held_item(&self) -> Option<&'a Item> {
pub fn held_item(&self) -> Option<&'own Item> {
self.held_item
}
pub fn has_held_item(&self, name: &StringKey) -> bool {
@ -211,7 +214,7 @@ impl<'a> Pokemon<'a> {
}
false
}
pub fn set_held_item(&mut self, item: &'a Item) {
pub fn set_held_item(&mut self, item: &'own Item) {
self.held_item = Some(item);
}
pub fn remove_held_item(&mut self) {
@ -221,10 +224,7 @@ impl<'a> Pokemon<'a> {
if self.held_item.is_none() {
return false;
}
let script = self
.library
.load_item_script(self.held_item.unwrap())
.unwrap();
let script = self.library.load_item_script(self.held_item.unwrap()).unwrap();
if script.is_none() {
return false;
}
@ -276,7 +276,7 @@ impl<'a> Pokemon<'a> {
&self.effort_values
}
pub fn get_battle(&self) -> Option<&Weak<RwLock<Battle<'a>>>> {
pub fn get_battle(&self) -> Option<&Weak<RwLock<Battle<'own, 'library>>>> {
if let Some(data) = &self.battle_data {
Some(&data.battle)
} else {
@ -305,7 +305,7 @@ impl<'a> Pokemon<'a> {
&self.ability_script
}
pub fn seen_opponents(&self) -> Option<&Vec<Weak<RwLock<Pokemon<'a>>>>> {
pub fn seen_opponents(&self) -> Option<&Vec<Weak<RwLock<Pokemon<'own, 'library>>>>> {
if let Some(data) = &self.battle_data {
Some(&data.seen_opponents)
} else {
@ -316,7 +316,7 @@ impl<'a> Pokemon<'a> {
self.allowed_experience
}
pub fn nature(&self) -> &'a Nature {
pub fn nature(&self) -> &'own Nature {
self.nature
}
@ -328,7 +328,7 @@ impl<'a> Pokemon<'a> {
self.boosted_stats = self.library.stat_calculator().calculate_boosted_stats(self);
}
pub fn change_species(&mut self, species: &'a Species, form: &'a Form) {
pub fn change_species(&mut self, species: &'own Species, form: &'own Form) {
self.species = species;
self.form = form;
@ -368,7 +368,7 @@ impl<'a> Pokemon<'a> {
}
}
pub fn change_form(&mut self, form: &'a Form) {
pub fn change_form(&mut self, form: &'own Form) {
if std::ptr::eq(self.form, form) {
return;
}
@ -409,10 +409,10 @@ impl<'a> Pokemon<'a> {
if let Some(battle_data) = &self.battle_data {
if let Some(battle) = battle_data.battle.upgrade() {
battle.read().event_hook().trigger(Event::FormChange {
pokemon: self,
form,
})
battle
.read()
.event_hook()
.trigger(Event::FormChange { pokemon: self, form })
}
}
}
@ -425,7 +425,7 @@ impl<'a> Pokemon<'a> {
self.current_health == 0
}
pub fn set_battle_data(&mut self, battle: Weak<RwLock<Battle<'a>>>, battle_side_index: u8) {
pub fn set_battle_data(&mut self, battle: Weak<RwLock<Battle<'own, 'library>>>, battle_side_index: u8) {
if let Some(battle_data) = &mut self.battle_data {
battle_data.battle = battle;
battle_data.battle_side_index = battle_side_index;
@ -465,7 +465,7 @@ impl<'a> Pokemon<'a> {
}
}
pub fn mark_opponent_as_seen(&mut self, pokemon: Weak<RwLock<Pokemon<'a>>>) {
pub fn mark_opponent_as_seen(&mut self, pokemon: Weak<RwLock<Pokemon<'own, 'library>>>) {
if let Some(battle_data) = &mut self.battle_data {
for seen_opponent in &battle_data.seen_opponents {
if seen_opponent.ptr_eq(&pokemon) {
@ -493,14 +493,7 @@ impl<'a> Pokemon<'a> {
new_health,
});
// TODO: register history
script_hook!(
on_damage,
self,
self,
source,
self.current_health,
new_health
);
script_hook!(on_damage, self, self, source, self.current_health, new_health);
}
}
self.current_health = new_health;
@ -512,10 +505,7 @@ impl<'a> Pokemon<'a> {
pub fn on_faint(&self, source: DamageSource) {
if let Some(battle_data) = &self.battle_data {
if let Some(battle) = battle_data.battle.upgrade() {
battle
.read()
.event_hook()
.trigger(Event::Faint { pokemon: self });
battle.read().event_hook().trigger(Event::Faint { pokemon: self });
script_hook!(on_faint, self, self, source);
script_hook!(on_remove, self,);
}
@ -536,13 +526,12 @@ impl<'a> Pokemon<'a> {
}
}
impl<'a> ScriptSource<'a> for Pokemon<'a> {
impl<'own, 'library> ScriptSource<'own> for Pokemon<'own, 'library> {
fn get_script_count(&self) -> usize {
let mut c = 3;
if let Some(battle_data) = &self.battle_data {
if let Some(battle) = battle_data.battle.upgrade() {
c += battle.read().sides()[battle_data.battle_side_index as usize]
.get_script_count();
c += battle.read().sides()[battle_data.battle_side_index as usize].get_script_count();
}
}
c
@ -563,14 +552,13 @@ impl<'a> ScriptSource<'a> for Pokemon<'a> {
self.get_own_scripts(scripts);
if let Some(battle_data) = &self.battle_data {
if let Some(battle) = battle_data.battle.upgrade() {
battle.read().sides()[battle_data.battle_side_index as usize]
.collect_scripts(scripts);
battle.read().sides()[battle_data.battle_side_index as usize].collect_scripts(scripts);
}
}
}
}
impl<'a> VolatileScripts<'a> for Pokemon<'a> {
impl<'own, 'library> VolatileScripts<'own> for Pokemon<'own, 'library> {
fn volatile_scripts(&self) -> &Arc<RwLock<ScriptSet>> {
&self.volatile
}

View File

@ -0,0 +1,40 @@
use crate::defines::LevelInt;
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
use crate::dynamic_data::models::pokemon::Pokemon;
use crate::static_data::{AbilityIndex, DataLibrary, Gender};
use crate::StringKey;
pub struct PokemonBuilder<'own> {
library: &'own DynamicLibrary,
species: StringKey,
level: LevelInt,
}
impl<'own> PokemonBuilder<'own> {
pub fn new(library: &'own DynamicLibrary, species: StringKey, level: LevelInt) -> Self {
Self {
library,
species,
level,
}
}
pub fn build<'func>(self) -> Pokemon<'own, 'own> {
let species = self.library.static_data().species().get(&self.species).unwrap();
let form = species.get_default_form();
Pokemon::new(
self.library,
species,
form,
AbilityIndex {
hidden: false,
index: 0,
},
self.level,
0,
Gender::Male,
0,
&"test_nature".into(),
)
}
}

View File

@ -2,11 +2,11 @@ use crate::dynamic_data::models::pokemon::Pokemon;
use std::sync::{Arc, RwLock};
#[derive(Debug)]
pub struct PokemonParty<'a> {
pokemon: Vec<Option<Arc<RwLock<Pokemon<'a>>>>>,
pub struct PokemonParty<'pokemon, 'library> {
pokemon: Vec<Option<Arc<RwLock<Pokemon<'pokemon, 'library>>>>>,
}
impl<'a> PokemonParty<'a> {
impl<'own, 'library> PokemonParty<'own, 'library> {
pub fn new(size: usize) -> Self {
let mut pokemon = Vec::with_capacity(size);
for _i in 0..size {
@ -15,7 +15,7 @@ impl<'a> PokemonParty<'a> {
Self { pokemon }
}
pub fn at(&self, index: usize) -> &Option<Arc<RwLock<Pokemon<'a>>>> {
pub fn at(&self, index: usize) -> &Option<Arc<RwLock<Pokemon<'own, 'library>>>> {
let opt = self.pokemon.get(index);
if let Some(v) = opt {
v
@ -31,8 +31,8 @@ impl<'a> PokemonParty<'a> {
pub fn swap_into(
&mut self,
index: usize,
pokemon: Option<Arc<RwLock<Pokemon<'a>>>>,
) -> Option<Arc<RwLock<Pokemon<'a>>>> {
pokemon: Option<Arc<RwLock<Pokemon<'own, 'library>>>>,
) -> Option<Arc<RwLock<Pokemon<'own, 'library>>>> {
if index >= self.pokemon.len() {
return pokemon;
}
@ -54,7 +54,7 @@ impl<'a> PokemonParty<'a> {
self.pokemon.len()
}
pub fn pokemon(&self) -> &Vec<Option<Arc<RwLock<Pokemon<'a>>>>> {
pub fn pokemon(&self) -> &Vec<Option<Arc<RwLock<Pokemon<'own, 'library>>>>> {
&self.pokemon
}

View File

@ -23,6 +23,52 @@ 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();
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),*) => {
for script in $source {
match script {
ScriptWrapper::Script(s) => {
if let Some(s) = s.upgrade() {
if let Some(s) = s.write().deref_mut() {
if !s.is_suppressed() {
s.$hook_name($($parameters),*);
}
}
}
}
ScriptWrapper::Set(s) => {
if let Some(s) = s.upgrade() {
for s in s.read().get_underlying() {
let mut s = s.1.get();
if let Some(s) = s.deref_mut() {
if !s.is_suppressed() {
s.$hook_name($($parameters),*);
}
}
}
}
}
}
}
};
}
#[derive(Default, Debug)]
pub struct ScriptSourceData {
is_initialized: bool,
@ -310,25 +356,11 @@ mod tests {
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
assert_eq!(
aggregator
.get_next()
.unwrap()
.get()
.as_mut()
.unwrap()
.name()
.str(),
aggregator.get_next().unwrap().get().as_mut().unwrap().name().str(),
"test_a"
);
assert_eq!(
aggregator
.get_next()
.unwrap()
.get()
.as_mut()
.unwrap()
.name()
.str(),
aggregator.get_next().unwrap().get().as_mut().unwrap().name().str(),
"test_b"
);
@ -352,14 +384,7 @@ mod tests {
let scripts = vec![ScriptWrapper::from(&set)];
let mut aggregator = ScriptAggregator::new(&scripts as *const Vec<ScriptWrapper>);
assert_eq!(
aggregator
.get_next()
.unwrap()
.get()
.as_mut()
.unwrap()
.name()
.str(),
aggregator.get_next().unwrap().get().as_mut().unwrap().name().str(),
"test_a"
);
@ -368,14 +393,7 @@ mod tests {
drop(mut_set);
assert_eq!(
aggregator
.get_next()
.unwrap()
.get()
.as_mut()
.unwrap()
.name()
.str(),
aggregator.get_next().unwrap().get().as_mut().unwrap().name().str(),
"test_c"
);
assert!(aggregator.get_next().is_none());

View File

@ -1,4 +1,4 @@
use crate::dynamic_data::choices::TurnChoice;
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;
@ -27,26 +27,27 @@ pub trait Script {
fn on_before_turn(&mut self, _choice: &TurnChoice) {}
fn change_speed(&mut self, _choice: &TurnChoice, _speed: &mut u32) {}
fn change_priority(&mut self, _choice: &TurnChoice, _priority: &mut i8) {}
fn change_move(&mut self, _choice: &TurnChoice, _move_name: &mut StringKey) {}
fn change_number_of_hits(&mut self, _choice: &TurnChoice, _number_of_hits: &mut u8) {}
fn change_move(&mut self, _choice: &MoveChoice, _move_name: &mut StringKey) {}
fn change_number_of_hits(&mut self, _choice: &MoveChoice, _number_of_hits: &mut u8) {}
fn prevent_move(&mut self, _move: &ExecutingMove, _prevent: &mut bool) {}
fn fail_move(&mut self, _move: &ExecutingMove, _fail: &mut bool) {}
fn stop_before_move(&mut self, _move: &ExecutingMove, _stop: &mut bool) {}
fn on_before_move(&mut self, _move: &ExecutingMove) {}
fn fail_incoming_move(&mut self, _move: &ExecutingMove, _target: &Pokemon, _fail: &mut bool) {}
fn is_invulnerable(
fn fail_incoming_move(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _fail: &mut bool) {}
fn is_invulnerable(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _invulnerable: &mut bool) {}
fn on_move_miss(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>) {}
fn change_move_type(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_invulnerable: &mut bool,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_move_type: &mut u8,
) {
}
fn on_move_miss(&mut self, _move: &ExecutingMove, _target: &Pokemon) {}
fn change_move_type(&mut self, _move: &ExecutingMove, _target: &Pokemon, _move_type: &mut u8) {}
fn change_effectiveness(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_effectiveness: &mut f32,
) {
@ -54,7 +55,7 @@ pub trait Script {
fn block_critical(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_block_critical: &mut bool,
) {
@ -62,7 +63,7 @@ pub trait Script {
fn block_incoming_critical(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_block_critical: &mut bool,
) {
@ -70,7 +71,7 @@ pub trait Script {
fn change_critical_stage(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_stage: &mut u8,
) {
@ -78,7 +79,7 @@ pub trait Script {
fn change_critical_modifier(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_modifier: &mut f32,
) {
@ -86,7 +87,7 @@ pub trait Script {
fn change_stab_modifier(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_modifier: &mut f32,
) {
@ -95,7 +96,7 @@ pub trait Script {
fn change_base_power(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_base_power: &mut u8,
) {
@ -103,48 +104,48 @@ pub trait Script {
fn change_damage_stats_user(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_stats_user: &mut Pokemon,
_stats_user: &mut Arc<RwLock<Pokemon>>,
) {
}
fn bypass_defensive_stat(
fn bypass_defensive_stat_boost(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_bypass: &mut bool,
) {
}
fn bypass_offensive_stat(
fn bypass_offensive_stat_boost(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_bypass: &mut bool,
) {
}
fn change_offensive_stat(
fn change_offensive_stat_value(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_amount: &mut f32,
_amount: &mut u32,
) {
}
fn change_defensive_stat(
fn change_defensive_stat_value(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_amount: &mut f32,
_amount: &mut u32,
) {
}
fn change_stat_modifier(
fn change_damage_stat_modifier(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_modifier: &mut f32,
) {
@ -152,29 +153,22 @@ pub trait Script {
fn change_damage_modifier(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_modifier: &mut f32,
) {
}
fn change_damage(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_hit: u8,
_damage: &mut u32,
) {
}
fn change_damage(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _hit: u8, _damage: &mut u32) {}
fn change_incoming_damage(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_damage: &mut u32,
) {
}
fn on_incoming_hit(&mut self, _move: &ExecutingMove, _target: &Pokemon, _hit: u8) {}
fn on_opponent_faints(&mut self, _move: &ExecutingMove, _target: &Pokemon, _hit: u8) {}
fn on_incoming_hit(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _hit: u8) {}
fn on_opponent_faints(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _hit: u8) {}
fn prevent_stat_boost_change(
&mut self,
_target: &Pokemon,
@ -195,7 +189,7 @@ pub trait Script {
fn prevent_secondary_effect(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_prevent: &mut bool,
) {
@ -203,21 +197,21 @@ pub trait Script {
fn change_effect_chance(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_chance: &mut f32,
) {
}
fn change_incoming_effect_change(
fn change_incoming_effect_chance(
&mut self,
_move: &ExecutingMove,
_target: &Pokemon,
_target: &Arc<RwLock<Pokemon>>,
_hit: u8,
_chance: &mut f32,
) {
}
fn on_secondary_effect(&mut self, _move: &ExecutingMove, _target: &Pokemon, _hit: u8) {}
fn on_after_hits(&mut self, _move: &ExecutingMove, _target: &Pokemon) {}
fn on_secondary_effect(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>, _hit: u8) {}
fn on_after_hits(&mut self, _move: &ExecutingMove, _target: &Arc<RwLock<Pokemon>>) {}
fn prevent_self_switch(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn prevent_opponent_switch(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn on_fail(&mut self, _target: &Pokemon) {}
@ -225,39 +219,14 @@ pub trait Script {
fn prevent_self_run_away(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn prevent_opponent_run_away(&mut self, _choice: &TurnChoice, _prevent: &mut bool) {}
fn on_end_turn(&mut self) {}
fn on_damage(
&mut self,
_pokemon: &Pokemon,
_source: DamageSource,
_old_health: u32,
_new_health: u32,
) {
}
fn on_damage(&mut self, _pokemon: &Pokemon, _source: DamageSource, _old_health: u32, _new_health: u32) {}
fn on_faint(&mut self, _pokemon: &Pokemon, _source: DamageSource) {}
fn on_switch_in(&mut self, _pokemon: &Pokemon) {}
fn on_after_held_item_consume(&mut self, _pokemon: &Pokemon, _item: &Item) {}
fn change_experience_gained(
&mut self,
_fainted_mon: &Pokemon,
_winning_mon: &Pokemon,
_amount: &mut u32,
) {
}
fn share_experience(
&mut self,
_fainted_mon: &Pokemon,
_winning_mon: &Pokemon,
_shares: &mut bool,
) {
}
fn change_experience_gained(&mut self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _amount: &mut u32) {}
fn share_experience(&mut self, _fainted_mon: &Pokemon, _winning_mon: &Pokemon, _shares: &mut bool) {}
fn block_weather(&mut self, _battle: &Battle, _blocked: &mut bool) {}
fn change_capture_rate_bonus(
&mut self,
_target: &Pokemon,
_pokeball: &Item,
_modifier: &mut u8,
) {
}
fn change_capture_rate_bonus(&mut self, _target: &Pokemon, _pokeball: &Item, _modifier: &mut u8) {}
fn as_any(&self) -> &dyn Any;
}

View File

@ -16,16 +16,11 @@ impl ScriptSet {
return lock.clone();
}
}
self.scripts
.insert(script.name().clone(), ScriptContainer::new(script));
self.scripts.insert(script.name().clone(), ScriptContainer::new(script));
self.scripts.last().unwrap().1.clone()
}
pub fn stack_or_add<'b, F>(
&mut self,
key: &StringKey,
instantiation: &'b F,
) -> PkmnResult<Option<ScriptContainer>>
pub fn stack_or_add<'b, F>(&mut self, key: &StringKey, instantiation: &'b F) -> PkmnResult<Option<ScriptContainer>>
where
F: Fn() -> PkmnResult<Option<Box<dyn Script>>>,
{
@ -76,4 +71,8 @@ impl ScriptSet {
pub fn count(&self) -> usize {
self.scripts.len()
}
pub(crate) fn get_underlying(&self) -> &IndexMap<StringKey, ScriptContainer> {
&self.scripts
}
}

View File

@ -3,9 +3,9 @@
#![feature(test)]
#![feature(bench_black_box)]
#![feature(let_chains)]
#![feature(once_cell)]
extern crate core;
extern crate lazy_static;
use crate::dynamic_data::libraries::script_resolver::ScriptCategory;
@ -19,10 +19,7 @@ pub mod utils;
#[derive(Debug, Clone)]
pub enum PokemonError {
ScriptNotFound {
category: ScriptCategory,
name: String,
},
ScriptNotFound { category: ScriptCategory, name: String },
MiscError,
InvalidTargetRequested,
}

View File

@ -43,11 +43,7 @@ pub mod tests {
let mut lib = AbilityLibrary::new(1);
lib.add(
&StringKey::new("test_ability"),
Box::new(Ability::new(
&"test_ability".into(),
&"test_ability".into(),
Vec::new(),
)),
Box::new(Ability::new(&"test_ability".into(), &"test_ability".into(), Vec::new())),
);
// Drops borrow as mut

View File

@ -39,7 +39,7 @@ pub mod tests {
use crate::static_data::moves::move_data::{MoveCategory, MoveData, MoveTarget};
use crate::static_data::moves::secondary_effect::SecondaryEffect;
use crate::StringKey;
use std::collections::HashSet;
use hashbrown::HashSet;
fn build_move() -> MoveData {
MoveData::new(

View File

@ -27,12 +27,7 @@ impl<'a> DataLibrary<'a, Box<Species<'a>>> for SpeciesLibrary<'a> {
&self.list
}
fn get_modify(
&mut self,
) -> (
&mut HashMap<StringKey, Box<Species<'a>>>,
&mut Vec<StringKey>,
) {
fn get_modify(&mut self) -> (&mut HashMap<StringKey, Box<Species<'a>>>, &mut Vec<StringKey>) {
(&mut self.map, &mut self.list)
}
}

View File

@ -73,8 +73,7 @@ pub mod test {
use crate::static_data::libraries::library_settings::LibrarySettings;
use crate::static_data::libraries::static_data::StaticData;
use crate::static_data::libraries::{
ability_library, growth_rate_library, item_library, move_library, species_library,
type_library,
ability_library, growth_rate_library, item_library, move_library, species_library, type_library,
};
use crate::static_data::natures;

View File

@ -1,8 +1,13 @@
use crate::static_data::SecondaryEffect;
use crate::StringKey;
use std::collections::HashSet;
use hashbrown::HashSet;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum MoveCategory {
Physical = 0,
Special = 1,
@ -10,6 +15,7 @@ pub enum MoveCategory {
}
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum MoveTarget {
Adjacent = 0,
AdjacentAlly,
@ -25,6 +31,7 @@ pub enum MoveTarget {
Any,
RandomOpponent,
#[cfg_attr(feature = "serde", serde(rename = "Self"))]
SelfUse,
}
@ -39,7 +46,7 @@ pub struct MoveData {
target: MoveTarget,
priority: i8,
secondary_effect: SecondaryEffect,
flags: HashSet<String>,
flags: HashSet<StringKey>,
}
impl MoveData {
@ -53,7 +60,7 @@ impl MoveData {
target: MoveTarget,
priority: i8,
secondary_effect: SecondaryEffect,
flags: HashSet<String>,
flags: HashSet<StringKey>,
) -> MoveData {
MoveData {
name: name.clone(),
@ -99,7 +106,11 @@ impl MoveData {
&self.secondary_effect
}
pub fn has_flag(&self, key: &str) -> bool {
pub fn has_secondary_effect(&self) -> bool {
self.secondary_effect.effect_name() != &StringKey::empty()
}
pub fn has_flag(&self, key: &StringKey) -> bool {
self.flags.contains(key)
}
}

View File

@ -1,3 +1,5 @@
use crate::StringKey;
#[derive(PartialEq, Debug)]
pub enum EffectParameter {
Bool(bool),
@ -9,7 +11,7 @@ pub enum EffectParameter {
#[derive(PartialEq, Debug)]
pub struct SecondaryEffect {
chance: f32,
effect_name: String,
effect_name: StringKey,
parameters: Vec<EffectParameter>,
}
@ -17,15 +19,11 @@ impl SecondaryEffect {
pub fn empty() -> SecondaryEffect {
SecondaryEffect {
chance: 0.0,
effect_name: "".to_string(),
effect_name: StringKey::empty(),
parameters: vec![],
}
}
pub fn new(
chance: f32,
effect_name: String,
parameters: Vec<EffectParameter>,
) -> SecondaryEffect {
pub fn new(chance: f32, effect_name: StringKey, parameters: Vec<EffectParameter>) -> SecondaryEffect {
SecondaryEffect {
chance,
effect_name,
@ -36,7 +34,7 @@ impl SecondaryEffect {
pub fn chance(&self) -> f32 {
self.chance
}
pub fn effect_name(&self) -> &str {
pub fn effect_name(&self) -> &StringKey {
&self.effect_name
}
pub fn parameters(&self) -> &Vec<EffectParameter> {
@ -53,11 +51,11 @@ mod tests {
fn create_secondary_effect() {
let empty = SecondaryEffect::empty();
assert_approx_eq!(empty.chance(), 0.0);
assert_eq!(empty.effect_name(), "");
assert_eq!(empty.effect_name(), &"".into());
assert_eq!(empty.parameters().len(), 0);
let set = SecondaryEffect::new(50.0, "foo".to_string(), Vec::new());
let set = SecondaryEffect::new(50.0, "foo".into(), Vec::new());
assert_approx_eq!(set.chance(), 50.0);
assert_eq!(set.effect_name(), "foo");
assert_eq!(set.effect_name(), &"foo".into());
assert_eq!(set.parameters().len(), 0);
}
}

View File

@ -95,10 +95,7 @@ pub mod tests {
#[test]
fn create_nature_library_insert_and_retrieve() {
let mut lib = NatureLibrary::new(2);
lib.load_nature(
"foo".into(),
Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
);
lib.load_nature("foo".into(), Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9));
lib.load_nature(
"bar".into(),
Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9),
@ -113,10 +110,7 @@ pub mod tests {
#[test]
fn create_nature_library_insert_and_get_name() {
let mut lib = NatureLibrary::new(2);
lib.load_nature(
"foo".into(),
Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9),
);
lib.load_nature("foo".into(), Nature::new(Statistic::HP, Statistic::Attack, 1.1, 0.9));
lib.load_nature(
"bar".into(),
Nature::new(Statistic::Attack, Statistic::Defense, 1.1, 0.9),

View File

@ -120,8 +120,7 @@ impl<'a> Form<'a> {
self.abilities[rand.get_between_unsigned(0, self.abilities.len() as u32) as usize]
}
pub fn get_random_hidden_ability(&self, rand: &mut Random) -> &Ability {
self.hidden_abilities
[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
self.hidden_abilities[rand.get_between_unsigned(0, self.hidden_abilities.len() as u32) as usize]
}
pub fn has_flag(&self, key: &StringKey) -> bool {

View File

@ -3,6 +3,7 @@ use crate::static_data::Gender;
use crate::Random;
use crate::StringKey;
use hashbrown::{HashMap, HashSet};
use std::lazy::SyncLazy;
#[derive(Debug)]
pub struct Species<'a> {
@ -14,9 +15,8 @@ pub struct Species<'a> {
forms: HashMap<StringKey, Form<'a>>,
flags: HashSet<StringKey>,
}
lazy_static::lazy_static! {
static ref DEFAULT_KEY: StringKey = StringKey::new("default");
}
static DEFAULT_KEY: SyncLazy<StringKey> = SyncLazy::new(|| StringKey::new("default"));
impl<'a> Species<'a> {
pub fn new(

View File

@ -18,14 +18,7 @@ impl<T> StatisticSet<T>
where
T: PrimInt,
{
pub fn new(
hp: T,
attack: T,
defense: T,
special_attack: T,
special_defense: T,
speed: T,
) -> Self {
pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
hp,
attack,
@ -117,21 +110,13 @@ impl<T, const MIN: i64, const MAX: i64> ClampedStatisticSet<T, MIN, MAX>
where
T: PrimInt,
{
pub fn new(
hp: T,
attack: T,
defense: T,
special_attack: T,
special_defense: T,
speed: T,
) -> Self {
pub fn new(hp: T, attack: T, defense: T, special_attack: T, special_defense: T, speed: T) -> Self {
Self {
hp: cast(clamp(cast::<T, i64>(hp).unwrap(), MIN, MAX)).unwrap(),
attack: cast(clamp(cast::<T, i64>(attack).unwrap(), MIN, MAX)).unwrap(),
defense: cast(clamp(cast::<T, i64>(defense).unwrap(), MIN, MAX)).unwrap(),
special_attack: cast(clamp(cast::<T, i64>(special_attack).unwrap(), MIN, MAX)).unwrap(),
special_defense: cast(clamp(cast::<T, i64>(special_defense).unwrap(), MIN, MAX))
.unwrap(),
special_defense: cast(clamp(cast::<T, i64>(special_defense).unwrap(), MIN, MAX)).unwrap(),
speed: cast(clamp(cast::<T, i64>(speed).unwrap(), MIN, MAX)).unwrap(),
}
}
@ -200,12 +185,8 @@ where
Statistic::HP => Self::change_stat(self.hp + value, &mut self.hp),
Statistic::Attack => Self::change_stat(self.attack + value, &mut self.attack),
Statistic::Defense => Self::change_stat(self.defense + value, &mut self.defense),
Statistic::SpecialAttack => {
Self::change_stat(self.special_attack + value, &mut self.special_attack)
}
Statistic::SpecialDefense => {
Self::change_stat(self.special_defense + value, &mut self.special_defense)
}
Statistic::SpecialAttack => Self::change_stat(self.special_attack + value, &mut self.special_attack),
Statistic::SpecialDefense => Self::change_stat(self.special_defense + value, &mut self.special_defense),
Statistic::Speed => Self::change_stat(self.speed + value, &mut self.speed),
}
}
@ -215,12 +196,8 @@ where
Statistic::HP => Self::change_stat(self.hp - value, &mut self.hp),
Statistic::Attack => Self::change_stat(self.attack - value, &mut self.attack),
Statistic::Defense => Self::change_stat(self.defense - value, &mut self.defense),
Statistic::SpecialAttack => {
Self::change_stat(self.special_attack - value, &mut self.special_attack)
}
Statistic::SpecialDefense => {
Self::change_stat(self.special_defense - value, &mut self.special_defense)
}
Statistic::SpecialAttack => Self::change_stat(self.special_attack - value, &mut self.special_attack),
Statistic::SpecialDefense => Self::change_stat(self.special_defense - value, &mut self.special_defense),
Statistic::Speed => Self::change_stat(self.speed - value, &mut self.speed),
}
}

View File

@ -1,5 +1,6 @@
use hashbrown::HashMap;
use std::hash::{Hash, Hasher};
use std::lazy::SyncLazy;
use std::sync::{Arc, Mutex, Weak};
/// StringKey is an immutable string that is used for indexing of hashmaps or equality a lot.
@ -12,9 +13,8 @@ pub struct StringKey {
hash: u32,
}
lazy_static::lazy_static! {
static ref STRING_CACHE: Mutex<HashMap<u32, Weak<str>>> = Mutex::new(HashMap::new());
}
static STRING_CACHE: SyncLazy<Mutex<HashMap<u32, Weak<str>>>> = SyncLazy::new(|| Mutex::new(HashMap::new()));
static EMPTY: SyncLazy<StringKey> = SyncLazy::new(|| StringKey::new(""));
impl StringKey {
pub const fn get_hash_const<const N: usize>(s: &[u8; N]) -> u32 {
@ -48,14 +48,15 @@ impl StringKey {
};
}
}
let v = Self {
str: s.into(),
hash,
};
let v = Self { str: s.into(), hash };
cache.insert(hash, Arc::downgrade(&v.str));
v
}
pub fn empty() -> Self {
EMPTY.clone()
}
pub fn str(&self) -> &str {
&self.str
}
@ -93,38 +94,35 @@ const fn to_lower(c: u8) -> u8 {
}
const CRC_TABLE: &[u32] = &[
0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148,
0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1,
0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75,
0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2,
0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162,
0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,
0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73,
0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3,
0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,
0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795,
0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,
0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE,
0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37,
0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
];
#[cfg(test)]

285
tests/data/Abilities.json Normal file
View File

@ -0,0 +1,285 @@
{
"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": {}
}

614
tests/data/GrowthRates.json Normal file
View File

@ -0,0 +1,614 @@
{
"Erratic": [
0,
15,
52,
122,
237,
406,
637,
942,
1326,
1800,
2369,
3041,
3822,
4719,
5737,
6881,
8155,
9564,
11111,
12800,
14632,
16610,
18737,
21012,
23437,
26012,
28737,
31610,
34632,
37800,
41111,
44564,
48155,
51881,
55737,
59719,
63822,
68041,
72369,
76800,
81326,
85942,
90637,
95406,
100237,
105122,
110052,
115015,
120001,
125000,
131324,
137795,
144410,
151165,
158056,
165079,
172229,
179503,
186894,
194400,
202013,
209728,
217540,
225443,
233431,
241496,
249633,
257834,
267406,
276458,
286328,
296358,
305767,
316074,
326531,
336255,
346965,
357812,
367807,
378880,
390077,
400293,
411686,
423190,
433572,
445239,
457001,
467489,
479378,
491346,
501878,
513934,
526049,
536557,
548720,
560922,
571333,
583539,
591882,
600000
],
"Fast": [
0,
6,
21,
51,
100,
172,
274,
409,
583,
800,
1064,
1382,
1757,
2195,
2700,
3276,
3930,
4665,
5487,
6400,
7408,
8518,
9733,
11059,
12500,
14060,
15746,
17561,
19511,
21600,
23832,
26214,
28749,
31443,
34300,
37324,
40522,
43897,
47455,
51200,
55136,
59270,
63605,
68147,
72900,
77868,
83058,
88473,
94119,
100000,
106120,
112486,
119101,
125971,
133100,
140492,
148154,
156089,
164303,
172800,
181584,
190662,
200037,
209715,
219700,
229996,
240610,
251545,
262807,
274400,
286328,
298598,
311213,
324179,
337500,
351180,
365226,
379641,
394431,
409600,
425152,
441094,
457429,
474163,
491300,
508844,
526802,
545177,
563975,
583200,
602856,
622950,
643485,
664467,
685900,
707788,
730138,
752953,
776239,
800000
],
"MediumFast": [
0,
8,
27,
64,
125,
216,
343,
512,
729,
1000,
1331,
1728,
2197,
2744,
3375,
4096,
4913,
5832,
6859,
8000,
9261,
10648,
12167,
13824,
15625,
17576,
19683,
21952,
24389,
27000,
29791,
32768,
35937,
39304,
42875,
46656,
50653,
54872,
59319,
64000,
68921,
74088,
79507,
85184,
91125,
97336,
103823,
110592,
117649,
125000,
132651,
140608,
148877,
157464,
166375,
175616,
185193,
195112,
205379,
216000,
226981,
238328,
250047,
262144,
274625,
287496,
300763,
314432,
328509,
343000,
357911,
373248,
389017,
405224,
421875,
438976,
456533,
474552,
493039,
512000,
531441,
551368,
571787,
592704,
614125,
636056,
658503,
681472,
704969,
729000,
753571,
778688,
804357,
830584,
857375,
884736,
912673,
941192,
970299,
1000000
],
"MediumSlow": [
0,
9,
57,
96,
135,
179,
236,
314,
419,
560,
742,
973,
1261,
1612,
2035,
2535,
3120,
3798,
4575,
5460,
6458,
7577,
8825,
10208,
11735,
13411,
15244,
17242,
19411,
21760,
24294,
27021,
29949,
33084,
36435,
40007,
43808,
47846,
52127,
56660,
61450,
66505,
71833,
77440,
83335,
89523,
96012,
102810,
109923,
117360,
125126,
133229,
141677,
150476,
159635,
169159,
179056,
189334,
199999,
211060,
222522,
234393,
246681,
259392,
272535,
286115,
300140,
314618,
329555,
344960,
360838,
377197,
394045,
411388,
429235,
447591,
466464,
485862,
505791,
526260,
547274,
568841,
590969,
613664,
636935,
660787,
685228,
710266,
735907,
762160,
789030,
816525,
844653,
873420,
902835,
932903,
963632,
995030,
1027103,
1059860
],
"Slow": [
0,
10,
33,
80,
156,
270,
428,
640,
911,
1250,
1663,
2160,
2746,
3430,
4218,
5120,
6141,
7290,
8573,
10000,
11576,
13310,
15208,
17280,
19531,
21970,
24603,
27440,
30486,
33750,
37238,
40960,
44921,
49130,
53593,
58320,
63316,
68590,
74148,
80000,
86151,
92610,
99383,
106480,
113906,
121670,
129778,
138240,
147061,
156250,
165813,
175760,
186096,
196830,
207968,
219520,
231491,
243890,
256723,
270000,
283726,
297910,
312558,
327680,
343281,
359370,
375953,
393040,
410636,
428750,
447388,
466560,
486271,
506530,
527343,
548720,
570666,
593190,
616298,
640000,
664301,
689210,
714733,
740880,
767656,
795070,
823128,
851840,
881211,
911250,
941963,
973360,
1005446,
1038230,
1071718,
1105920,
1140841,
1176490,
1212873,
1250000
],
"Fluctuating": [
0,
4,
13,
32,
65,
112,
178,
276,
393,
540,
745,
967,
1230,
1591,
1957,
2457,
3046,
3732,
4526,
5440,
6482,
7666,
9003,
10506,
12187,
14060,
16140,
18439,
20974,
23760,
26811,
30146,
33780,
37731,
42017,
46656,
50653,
55969,
60505,
66560,
71677,
78533,
84277,
91998,
98415,
107069,
114205,
123863,
131766,
142500,
151222,
163105,
172697,
185807,
196322,
210739,
222231,
238036,
250562,
267840,
281456,
300293,
315059,
335544,
351520,
373744,
390991,
415050,
433631,
459620,
479600,
507617,
529063,
559209,
582187,
614566,
639146,
673863,
700115,
737280,
765275,
804997,
834809,
877201,
908905,
954084,
987754,
1035837,
1071552,
1122660,
1160499,
1214753,
1254796,
1312322,
1354652,
1415577,
1460276,
1524731,
1571884,
1640000
]
}

5913
tests/data/Items.json Normal file

File diff suppressed because it is too large Load Diff

10400
tests/data/Moves.json Normal file

File diff suppressed because it is too large Load Diff

26
tests/data/Natures.csv Normal file
View File

@ -0,0 +1,26 @@
Name|Increased|Decreased
Hardy||
Lonely|Attack|Defense
Brave|Attack|Speed
Adamant|Attack|SpecialAttack
Naughty|Attack|SpecialDefense
Bold|Defense|Attack
Docile||
Relaxed|Defense|Speed
Impish|Defense|SpecialAttack
Lax|Defense|SpecialDefense
Timid|Speed|Attack
Hasty|Speed|Defense
Serious||
Jolly|Speed|SpecialAttack
Naive|Speed|SpecialDefense
Modest|SpecialAttack|Attack
Mild|SpecialAttack|Defense
Quiet|SpecialAttack|Speed
Bashful||
Rash|SpecialAttack|SpecialDefense
Calm|SpecialDefense|Attack
Gentle|SpecialDefense|Defense
Sassy|SpecialDefense|Speed
Careful|SpecialDefense|SpecialAttack
Quirky||
1 Name Increased Decreased
2 Hardy
3 Lonely Attack Defense
4 Brave Attack Speed
5 Adamant Attack SpecialAttack
6 Naughty Attack SpecialDefense
7 Bold Defense Attack
8 Docile
9 Relaxed Defense Speed
10 Impish Defense SpecialAttack
11 Lax Defense SpecialDefense
12 Timid Speed Attack
13 Hasty Speed Defense
14 Serious
15 Jolly Speed SpecialAttack
16 Naive Speed SpecialDefense
17 Modest SpecialAttack Attack
18 Mild SpecialAttack Defense
19 Quiet SpecialAttack Speed
20 Bashful
21 Rash SpecialAttack SpecialDefense
22 Calm SpecialDefense Attack
23 Gentle SpecialDefense Defense
24 Sassy SpecialDefense Speed
25 Careful SpecialDefense SpecialAttack
26 Quirky

86767
tests/data/Pokemon.json Normal file

File diff suppressed because it is too large Load Diff

19
tests/data/Types.csv Normal file
View File

@ -0,0 +1,19 @@
Types|Normal|Fighting|Flying|Poison|Ground|Rock|Bug|Ghost|Steel|Fire|Water|Grass|Electric|Psychic|Ice|Dragon|Dark|Fairy
Normal|1|1|1|1|1|0.5|1|0|0.5|1|1|1|1|1|1|1|1|1
Fighting|2|1|0.5|0.5|1|2|0.5|0|2|1|1|1|1|0.5|2|1|2|0.5
Flying|1|2|1|1|1|0.5|2|1|0.5|1|1|2|0.5|1|1|1|1|1
Poison|1|1|1|0.5|0.5|0.5|1|0.5|0|1|1|2|1|1|1|1|1|2
Ground|1|1|0|2|1|2|0.5|1|2|2|1|0.5|2|1|1|1|1|1
Rock|1|0.5|2|1|0.5|1|2|1|0.5|2|1|1|1|1|2|1|1|1
Bug|1|0.5|0.5|0.5|1|1|1|0.5|0.5|0.5|1|2|1|2|1|1|2|0.5
Ghost|0|1|1|1|1|1|1|2|1|1|1|1|1|2|1|1|0.5|1
Steel|1|1|1|1|1|2|1|1|0.5|0.5|0.5|1|0.5|1|2|1|1|2
Fire|1|1|1|1|1|0.5|2|1|2|0.5|0.5|2|1|1|2|0.5|1|1
Water|1|1|1|1|2|2|1|1|1|2|0.5|0.5|1|1|1|0.5|1|1
Grass|1|1|0.5|0.5|2|2|0.5|1|0.5|0.5|2|0.5|1|1|1|0.5|1|1
Electric|1|1|2|1|0|1|1|1|1|1|2|0.5|0.5|1|1|0.5|1|1
Psychic|1|2|1|2|1|1|1|1|0.5|1|1|1|1|0.5|1|1|0|1
Ice|1|1|2|1|2|1|1|1|0.5|0.5|0.5|2|1|1|0.5|2|1|1
Dragon|1|1|1|1|1|1|1|1|0.5|1|1|1|1|1|1|2|1|0
Dark|1|0.5|1|1|1|1|1|2|1|1|1|1|1|2|1|1|0.5|0.5
Fairy|1|2|1|0.5|1|1|1|1|0.5|0.5|1|1|1|1|1|2|2|1
1 Types Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Fire Water Grass Electric Psychic Ice Dragon Dark Fairy
2 Normal 1 1 1 1 1 0.5 1 0 0.5 1 1 1 1 1 1 1 1 1
3 Fighting 2 1 0.5 0.5 1 2 0.5 0 2 1 1 1 1 0.5 2 1 2 0.5
4 Flying 1 2 1 1 1 0.5 2 1 0.5 1 1 2 0.5 1 1 1 1 1
5 Poison 1 1 1 0.5 0.5 0.5 1 0.5 0 1 1 2 1 1 1 1 1 2
6 Ground 1 1 0 2 1 2 0.5 1 2 2 1 0.5 2 1 1 1 1 1
7 Rock 1 0.5 2 1 0.5 1 2 1 0.5 2 1 1 1 1 2 1 1 1
8 Bug 1 0.5 0.5 0.5 1 1 1 0.5 0.5 0.5 1 2 1 2 1 1 2 0.5
9 Ghost 0 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 0.5 1
10 Steel 1 1 1 1 1 2 1 1 0.5 0.5 0.5 1 0.5 1 2 1 1 2
11 Fire 1 1 1 1 1 0.5 2 1 2 0.5 0.5 2 1 1 2 0.5 1 1
12 Water 1 1 1 1 2 2 1 1 1 2 0.5 0.5 1 1 1 0.5 1 1
13 Grass 1 1 0.5 0.5 2 2 0.5 1 0.5 0.5 2 0.5 1 1 1 0.5 1 1
14 Electric 1 1 2 1 0 1 1 1 1 1 2 0.5 0.5 1 1 0.5 1 1
15 Psychic 1 2 1 2 1 1 1 1 0.5 1 1 1 1 0.5 1 1 0 1
16 Ice 1 1 2 1 2 1 1 1 0.5 0.5 0.5 2 1 1 0.5 2 1 1
17 Dragon 1 1 1 1 1 1 1 1 0.5 1 1 1 1 1 1 2 1 0
18 Dark 1 0.5 1 1 1 1 1 2 1 1 1 1 1 2 1 1 0.5 0.5
19 Fairy 1 2 1 0.5 1 1 1 1 0.5 0.5 1 1 1 1 1 2 2 1

141
tests/library_loader.rs Normal file
View File

@ -0,0 +1,141 @@
use hashbrown::HashSet;
use pkmn_lib::static_data::{DataLibrary, EffectParameter, MoveData, MoveLibrary, SecondaryEffect, TypeLibrary};
use pkmn_lib::StringKey;
use project_root::get_project_root;
use serde_json::Value;
use std::fs::File;
use std::io::Read;
pub fn load_types(path: &String) -> TypeLibrary {
let mut type_library = TypeLibrary::new(18);
let mut reader = csv::ReaderBuilder::new()
.delimiter(b'|')
.from_path(path.to_string() + "Types.csv")
.unwrap();
let headers = reader.headers().unwrap();
for header in headers.iter().skip(1) {
type_library.register_type(&StringKey::new(header.clone()));
}
for record in reader.records() {
let record = record.unwrap();
let offensive_type = record.get(0).unwrap();
let offensive_type_id = type_library.get_type_id(&StringKey::new(offensive_type.clone()));
for (i, v) in record.iter().skip(1).enumerate() {
let effectiveness = v.parse::<f32>().unwrap();
type_library.set_effectiveness(offensive_type_id, i as u8, effectiveness);
}
}
type_library
}
pub fn load_moves(path: &String, type_library: &TypeLibrary) -> MoveLibrary {
let mut file = File::open(path.to_string() + "Moves.json").unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
println!("1");
let json: Value = serde_json::from_str(&data).unwrap();
println!("a");
let data = json.as_object().unwrap().get("data").unwrap().as_array().unwrap();
println!("this");
let mut move_library = MoveLibrary::new(data.len());
println!("Heeere");
for move_data in data {
let move_data = move_data.as_object().unwrap();
let move_name = StringKey::new(move_data["name"].as_str().unwrap().clone());
println!("Loaded move {:?}", move_name);
let move_type = StringKey::new(move_data["type"].as_str().unwrap());
let move_type_id = type_library.get_type_id(&move_type);
let move_category = serde_json::from_value(move_data["category"].clone()).unwrap();
let base_power = move_data["power"].as_i64().unwrap() as u8;
let accuracy = move_data["accuracy"].as_i64().unwrap() as u8;
let pp = move_data["pp"].as_i64().unwrap() as u8;
let target = serde_json::from_value(move_data["target"].clone()).unwrap();
let priority = move_data["priority"].as_i64().unwrap() as i8;
let secondary_effect = if let Some(v) = move_data.get("effect") {
let mut chance = -1.0;
if let Some(chance_value) = v.get("chance") {
chance = chance_value.as_f64().unwrap() as f32;
}
let mut parameters = Vec::new();
if let Some(pars) = v.get("parameters") {
let pars = pars.as_array().unwrap();
for par in pars {
match par {
Value::Null => {
panic!("Unexpected type")
}
Value::Bool(b) => {
parameters.push(EffectParameter::Bool(*b));
}
Value::Number(n) => {
if n.is_f64() {
parameters.push(EffectParameter::Float(n.as_f64().unwrap() as f32));
} else {
parameters.push(EffectParameter::Int(n.as_i64().unwrap()));
}
}
Value::String(s) => {
parameters.push(EffectParameter::String(s.clone()));
}
Value::Array(_) => {
panic!("Unexpected type")
}
Value::Object(_) => {
panic!("Unexpected type")
}
}
}
}
SecondaryEffect::new(chance, StringKey::new(v["name"].as_str().unwrap().clone()), parameters)
} else {
SecondaryEffect::empty()
};
let mut flags = HashSet::new();
if let Some(f) = move_data.get("flags") {
let f = f.as_array().unwrap();
for flag in f {
flags.insert(StringKey::new(flag.as_str().unwrap()));
}
}
move_library.add(
&move_name,
MoveData::new(
&move_name.clone(),
move_type_id,
move_category,
base_power,
accuracy,
pp,
target,
priority,
secondary_effect,
flags,
),
);
}
move_library
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_type_library_loaded() {
let mut path = get_project_root().unwrap();
path.push("tests/data/");
let lib = load_types(&path.to_str().unwrap().to_string());
assert_eq!(
lib.get_effectiveness(
lib.get_type_id(&StringKey::new("fire")),
&vec![lib.get_type_id(&StringKey::new("grass")),]
),
2.0
);
}

13
tests/tests.rs Normal file
View File

@ -0,0 +1,13 @@
use project_root::get_project_root;
pub mod library_loader;
#[test]
#[cfg_attr(miri, ignore)]
fn run_integration_tests() {
let mut path = get_project_root().unwrap();
path.push("tests/data/");
let path = path.to_str().unwrap().to_string();
let type_library = library_loader::load_types(&path);
let move_library = library_loader::load_moves(&path, &type_library);
}