use std::ops::Deref; use std::sync::Arc; use num_traits::abs; use crate::dynamic_data::Battle; use crate::dynamic_data::Pokemon; use crate::static_data::MoveTarget; /// Helper type for the vector of targets we will return. pub type TargetList = Vec>>; /// This returns all Pokemon in the battle. fn get_all_targets(battle: &Battle) -> TargetList { 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().deref() { v.push(pokemon.as_ref().cloned()); } } v } /// Little helper function to get the other side. 1 --> 0, 0 --> 1 fn get_opposite_side(side: u8) -> u8 { if side == 0 { return 1; } 0 } /// Gets all Pokemon that are adjacent to of directly opposite of a Pokemon. This means the target, /// the Pokemon left of it, the Pokemon right of it, and the Pokemon opposite of it. fn get_all_adjacent_opponent(side: u8, index: u8, battle: &Battle) -> TargetList { 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), left as u8) .as_ref() .cloned(), battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(), battle.get_pokemon(get_opposite_side(side), right).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), left as u8) .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(), battle.get_pokemon(get_opposite_side(side), right).as_ref().cloned(), ] } /// Gets all Pokemon that are adjacent to a Pokemon. This includes the target, the Pokemon to the /// left of it, and the Pokemon to the right of it. fn get_all_adjacent(side: u8, index: u8, battle: &Battle) -> TargetList { 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(), ] } /// Gets the target for a specific move target type, given the targeted position. pub fn resolve_targets(side: u8, index: u8, target: MoveTarget, battle: &Battle) -> TargetList { match target { // These all resolve to a single position. We let the client deal with where the target is, // and just return the Pokemon at that given target here. MoveTarget::Adjacent | MoveTarget::AdjacentAlly | MoveTarget::AdjacentAllySelf | MoveTarget::AdjacentOpponent | MoveTarget::Any | MoveTarget::RandomOpponent | MoveTarget::SelfUse => { vec![battle.get_pokemon(side, index).as_ref().cloned()] } // If all pokemon are requested, give all Pokemon on the battlefield MoveTarget::All => get_all_targets(battle), // If the adjacent Pokemon are requested, pass those. MoveTarget::AllAdjacent => get_all_adjacent(side, index, battle), // If the adjacent and opponent Pokemon are requested, give those. MoveTarget::AllAdjacentOpponent => get_all_adjacent_opponent(side, index, battle), // If all Pokemon on a side are requested, simply give all Pokemon on the side back, and let // the client deal with what side is passed. MoveTarget::AllAlly | MoveTarget::AllOpponent => { let mut v = Vec::new(); for pokemon in battle.sides()[side as usize].pokemon().deref() { v.push(pokemon.as_ref().cloned()); } v } } } /// Checks whether a given side and index are valid for a given move target and user. pub fn is_valid_target(side: u8, index: u8, target: MoveTarget, user: &Pokemon) -> bool { let user_side = user.get_battle_side_index(); let user_index = user.get_battle_index(); // 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, } }