From a33369afcc62914d24bd333a17209a49cba731d6 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Tue, 14 Jun 2022 19:11:24 +0200 Subject: [PATCH] Some more functionality for battling, check if choice is valid, implement target resolvers. --- src/dynamic_data/choices/mod.rs | 16 +- src/dynamic_data/flow/mod.rs | 1 + src/dynamic_data/flow/target_resolver.rs | 169 ++++++++++++++++++++++ src/dynamic_data/models/battle.rs | 43 ++++++ src/dynamic_data/models/executing_move.rs | 2 +- src/dynamic_data/models/learned_move.rs | 40 ++++- src/dynamic_data/models/pokemon.rs | 2 +- src/static_data/moves/move_data.rs | 42 +++++- 8 files changed, 305 insertions(+), 10 deletions(-) create mode 100644 src/dynamic_data/flow/target_resolver.rs diff --git a/src/dynamic_data/choices/mod.rs b/src/dynamic_data/choices/mod.rs index 0ba0d79..4a9cf07 100644 --- a/src/dynamic_data/choices/mod.rs +++ b/src/dynamic_data/choices/mod.rs @@ -5,7 +5,18 @@ use crate::dynamic_data::models::pokemon::Pokemon; pub enum TurnChoice<'a> { Move { user: &'a Pokemon<'a>, - used_move: Box, + used_move: Box>, + target_side: u8, + target_index: u8, + }, + Item { + user: &'a Pokemon<'a>, + }, + Switch { + user: &'a Pokemon<'a>, + }, + Flee { + user: &'a Pokemon<'a>, }, } @@ -13,6 +24,9 @@ impl<'a> TurnChoice<'a> { pub fn user(&self) -> &'a Pokemon<'a> { match self { TurnChoice::Move { user, .. } => user, + TurnChoice::Item { user, .. } => user, + TurnChoice::Switch { user, .. } => user, + TurnChoice::Flee { user, .. } => user, } } } diff --git a/src/dynamic_data/flow/mod.rs b/src/dynamic_data/flow/mod.rs index ac4fa1e..481a3b5 100644 --- a/src/dynamic_data/flow/mod.rs +++ b/src/dynamic_data/flow/mod.rs @@ -1 +1,2 @@ pub mod choice_queue; +pub mod target_resolver; diff --git a/src/dynamic_data/flow/target_resolver.rs b/src/dynamic_data/flow/target_resolver.rs new file mode 100644 index 0000000..e4722bf --- /dev/null +++ b/src/dynamic_data/flow/target_resolver.rs @@ -0,0 +1,169 @@ +use crate::dynamic_data::models::battle::Battle; +use crate::dynamic_data::models::pokemon::Pokemon; +use crate::static_data::MoveTarget; +use num_traits::abs; +use parking_lot::RwLock; +use std::sync::Arc; + +pub type TargetList<'a> = Vec>>>>; + +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); + for side in battle.sides() { + for pokemon in side.pokemon() { + v.push(pokemon.as_ref().cloned()); + } + } + v +} + +fn get_opposite_side(side: u8) -> u8 { + if side == 0 { + return 1; + } + 0 +} + +fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> { + 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(), + ]; + } + if left >= 0 { + return if right < battle.pokemon_per_side() { + vec![ + 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(), + ] + } 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(), + ] + }; + } + + 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(), + ] +} + +fn get_all_adjacent_opponent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> { + 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()]; + } + if left >= 0 { + if right < battle.pokemon_per_side() { + return vec![ + 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(), + ]; + } + return vec![ + battle.get_pokemon(side, index).as_ref().cloned(), + battle.get_pokemon(side, left as u8).as_ref().cloned(), + ]; + } + vec![ + battle.get_pokemon(side, index).as_ref().cloned(), + battle.get_pokemon(side, right).as_ref().cloned(), + ] +} + +pub fn resolve_targets<'b>( + side: u8, + index: u8, + target: MoveTarget, + battle: &Battle<'b>, +) -> TargetList<'b> { + match target { + MoveTarget::Adjacent + | MoveTarget::AdjacentAlly + | MoveTarget::AdjacentAllySelf + | MoveTarget::AdjacentOpponent + | MoveTarget::Any + | MoveTarget::RandomOpponent + | MoveTarget::SelfUse => { + vec![battle.get_pokemon(side, index).as_ref().cloned()] + } + MoveTarget::All => get_all_targets(battle), + MoveTarget::AllAdjacent => get_all_adjacent(side, index, battle), + MoveTarget::AllAdjacentOpponent => get_all_adjacent_opponent(side, index, battle), + MoveTarget::AllAlly | MoveTarget::AllOpponent => { + let mut v = Vec::new(); + for pokemon in battle.sides()[side as usize].pokemon() { + v.push(pokemon.as_ref().cloned()); + } + v + } + } +} + +pub fn is_valid_target(side: u8, index: u8, target: MoveTarget, user: &Pokemon) -> bool { + let user_side = user.get_battle_side_index(); + let user_index = user.get_battle_index(); + // If the user is not on the field, nothing is a valid target + if user_side.is_none() || user_index.is_none() { + return false; + } + + match target { + MoveTarget::Adjacent | MoveTarget::AllAdjacent => { + let diff = abs(index as i32 - user_index.unwrap() as i32); + if diff == 0 { + return user_side.unwrap() != side; + } + diff <= 1 + } + MoveTarget::AdjacentAlly => { + if user_side.unwrap() != side { + false + } else { + abs(index as i32 - user_index.unwrap() as i32) == 1 + } + } + MoveTarget::AdjacentAllySelf => { + if user_side.unwrap() != side { + false + } else { + abs(index as i32 - user_index.unwrap() as i32) <= 1 + } + } + MoveTarget::AdjacentOpponent | MoveTarget::AllAdjacentOpponent => { + if user_side.unwrap() == side { + false + } else { + abs(index as i32 - user_index.unwrap() as i32) <= 1 + } + } + MoveTarget::All | MoveTarget::Any | MoveTarget::RandomOpponent => true, + MoveTarget::AllAlly => user_side.unwrap() == side, + MoveTarget::AllOpponent => user_side.unwrap() != side, + MoveTarget::SelfUse => user_side.unwrap() == side && user_index.unwrap() == index, + } +} diff --git a/src/dynamic_data/models/battle.rs b/src/dynamic_data/models/battle.rs index 8ffe218..e414a55 100644 --- a/src/dynamic_data/models/battle.rs +++ b/src/dynamic_data/models/battle.rs @@ -1,11 +1,14 @@ +use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::event_hook::EventHook; use crate::dynamic_data::flow::choice_queue::ChoiceQueue; +use crate::dynamic_data::flow::target_resolver::is_valid_target; use crate::dynamic_data::history::history_holder::HistoryHolder; use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary; use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::battle_random::BattleRandom; use crate::dynamic_data::models::battle_result::BattleResult; use crate::dynamic_data::models::battle_side::BattleSide; +use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::script_handling::script::Script; use crate::dynamic_data::script_handling::script_set::ScriptSet; use crate::dynamic_data::script_handling::volatile_scripts::VolatileScripts; @@ -123,6 +126,18 @@ impl<'a> Battle<'a> { &self.current_turn_queue } + pub fn get_pokemon(&self, side: u8, index: u8) -> &Option>>> { + let side = self.sides.get(side as usize); + if side.is_none() { + return &None; + } + let pokemon = side.unwrap().pokemon().get(index as usize); + if pokemon.is_none() { + return &None; + } + pokemon.unwrap() + } + pub fn can_slot_be_filled(&self, side: u8, index: u8) -> bool { for party in &self.parties { if party.is_responsible_for_index(side, index) && party.has_pokemon_not_in_field() { @@ -165,6 +180,34 @@ impl<'a> Battle<'a> { } self.has_ended = true; } + + 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() { + return false; + } + if let TurnChoice::Move { + used_move, + target_side, + target_index, + user, + } = choice + { + // TODO: Hook to change number of PP needed. + if used_move.remaining_pp() < 1 { + return false; + } + if !is_valid_target( + *target_side, + *target_index, + used_move.move_data().target(), + user, + ) { + return false; + } + } + true + } } impl<'a> VolatileScripts<'a> for Battle<'a> { diff --git a/src/dynamic_data/models/executing_move.rs b/src/dynamic_data/models/executing_move.rs index a7ac5d0..ca4a947 100644 --- a/src/dynamic_data/models/executing_move.rs +++ b/src/dynamic_data/models/executing_move.rs @@ -61,7 +61,7 @@ pub struct ExecutingMove<'a, 'b> { number_of_hits: u8, hits: Vec, user: &'a Pokemon<'b>, - chosen_move: &'a LearnedMove, + chosen_move: &'a LearnedMove<'a>, use_move: &'a MoveData, script: ScriptContainer, targets: Vec>>, diff --git a/src/dynamic_data/models/learned_move.rs b/src/dynamic_data/models/learned_move.rs index 40d36d3..b63562d 100644 --- a/src/dynamic_data/models/learned_move.rs +++ b/src/dynamic_data/models/learned_move.rs @@ -1,2 +1,40 @@ +use crate::static_data::MoveData; + #[derive(Debug)] -pub struct LearnedMove {} +pub struct LearnedMove<'a> { + move_data: &'a MoveData, + max_pp: u8, + remaining_pp: u8, + learn_method: MoveLearnMethod, +} + +#[derive(Copy, Clone, Debug)] +pub enum MoveLearnMethod { + Unknown = 0, + Level = 1, +} + +impl<'a> LearnedMove<'a> { + pub fn new(move_data: &'a MoveData, learn_method: MoveLearnMethod) -> Self { + Self { + move_data, + max_pp: move_data.base_usages(), + remaining_pp: move_data.base_usages(), + learn_method, + } + } + + pub fn move_data(&self) -> &MoveData { + self.move_data + } + + pub fn max_pp(&self) -> u8 { + self.max_pp + } + pub fn remaining_pp(&self) -> u8 { + self.remaining_pp + } + pub fn learn_method(&self) -> MoveLearnMethod { + self.learn_method + } +} diff --git a/src/dynamic_data/models/pokemon.rs b/src/dynamic_data/models/pokemon.rs index d117fa8..66511a0 100644 --- a/src/dynamic_data/models/pokemon.rs +++ b/src/dynamic_data/models/pokemon.rs @@ -81,7 +81,7 @@ pub struct Pokemon<'a> { battle_data: Option>, - moves: [Option; MAX_MOVES], + moves: [Option>; MAX_MOVES], allowed_experience: bool, types: Vec, diff --git a/src/static_data/moves/move_data.rs b/src/static_data/moves/move_data.rs index e97eec4..eb71f91 100644 --- a/src/static_data/moves/move_data.rs +++ b/src/static_data/moves/move_data.rs @@ -2,16 +2,16 @@ use crate::static_data::SecondaryEffect; use crate::StringKey; use std::collections::HashSet; -#[derive(PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum MoveCategory { - Physical, - Special, - Status, + Physical = 0, + Special = 1, + Status = 2, } -#[derive(PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum MoveTarget { - Adjacent, + Adjacent = 0, AdjacentAlly, AdjacentAllySelf, AdjacentOpponent, @@ -69,6 +69,36 @@ impl MoveData { } } + pub fn name(&self) -> &StringKey { + &self.name + } + pub fn move_type(&self) -> u8 { + self.move_type + } + pub fn category(&self) -> MoveCategory { + self.category + } + pub fn base_power(&self) -> u8 { + self.base_power + } + pub fn accuracy(&self) -> u8 { + self.accuracy + } + pub fn base_usages(&self) -> u8 { + self.base_usages + } + pub fn target(&self) -> MoveTarget { + self.target + } + + pub fn priority(&self) -> i8 { + self.priority + } + + pub fn secondary_effect(&self) -> &SecondaryEffect { + &self.secondary_effect + } + pub fn has_flag(&self, key: &str) -> bool { self.flags.contains(key) }