Some more functionality for battling, check if choice is valid, implement target resolvers.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2022-06-14 19:11:24 +02:00
parent 8746f03500
commit a33369afcc
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
8 changed files with 305 additions and 10 deletions

View File

@ -5,7 +5,18 @@ use crate::dynamic_data::models::pokemon::Pokemon;
pub enum TurnChoice<'a> {
Move {
user: &'a Pokemon<'a>,
used_move: Box<LearnedMove>,
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>,
},
}
@ -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,
}
}
}

View File

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

View File

@ -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<Option<Arc<RwLock<Pokemon<'a>>>>>;
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,
}
}

View File

@ -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<Arc<RwLock<Pokemon<'a>>>> {
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> {

View File

@ -61,7 +61,7 @@ pub struct ExecutingMove<'a, 'b> {
number_of_hits: u8,
hits: Vec<HitData>,
user: &'a Pokemon<'b>,
chosen_move: &'a LearnedMove,
chosen_move: &'a LearnedMove<'a>,
use_move: &'a MoveData,
script: ScriptContainer,
targets: Vec<Option<&'a Pokemon<'b>>>,

View File

@ -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
}
}

View File

@ -81,7 +81,7 @@ pub struct Pokemon<'a> {
battle_data: Option<PokemonBattleData<'a>>,
moves: [Option<LearnedMove>; MAX_MOVES],
moves: [Option<LearnedMove<'a>>; MAX_MOVES],
allowed_experience: bool,
types: Vec<u8>,

View File

@ -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)
}