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

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

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)]