use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::event_hook::Event; use crate::dynamic_data::models::battle::Battle; use crate::dynamic_data::models::battle_party::BattleParty; 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; use crate::dynamic_data::script_handling::ScriptSource; use crate::{script_hook, PkmnResult, StringKey}; use std::ops::Deref; use std::sync::{Arc, RwLock, Weak}; #[derive(Debug)] pub struct BattleSide<'a> { index: u8, pokemon_per_side: u8, pokemon: Vec>>>>, choices: Vec>>>, fillable_slots: Vec, choices_set: u8, battle: Weak>>, has_fled_battle: bool, volatile_scripts: Arc>, } impl<'a> BattleSide<'a> { pub fn new(index: u8, battle: Weak>>, 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); for i in 0..pokemon_per_side { pokemon[i as usize] = None; choices[i as usize] = None; fillable_slots[i as usize] = true; } Self { index, pokemon_per_side, pokemon, choices, fillable_slots, choices_set: 0, battle, has_fled_battle: false, volatile_scripts: Default::default(), } } pub fn index(&self) -> u8 { self.index } pub fn pokemon_per_side(&self) -> u8 { self.pokemon_per_side } pub fn pokemon(&self) -> &Vec>>>> { &self.pokemon } pub fn choices(&self) -> &Vec>>> { &self.choices } pub fn fillable_slots(&self) -> &Vec { &self.fillable_slots } pub fn choices_set(&self) -> u8 { self.choices_set } pub fn battle(&self) -> &Weak>> { &self.battle } pub fn has_fled_battle(&self) -> bool { self.has_fled_battle } pub fn volatile_scripts(&self) -> &Arc> { &self.volatile_scripts } pub fn all_choices_set(&self) -> bool { self.choices_set == self.pokemon_per_side } /// Returns true if there are slots that need to be filled with a new pokemon, that have parties /// responsible for them. Returns false if all slots are filled with usable pokemon, or slots are /// empty, but can't be filled by any party anymore. pub fn all_slots_filled(&self) -> bool { for (i, pokemon) in self.pokemon.iter().enumerate() { if (!pokemon.is_none() || !pokemon.as_ref().unwrap().read().unwrap().is_usable()) && self .battle .upgrade() .unwrap() .read() .unwrap() .can_slot_be_filled(self.index, i as u8) { return false; } } true } pub fn set_choice(&mut self, choice: TurnChoice<'a>) { for (index, pokemon_slot) in self.pokemon.iter().enumerate() { if let Some(pokemon) = pokemon_slot { if pokemon.read().unwrap().unique_identifier() == choice.user().unique_identifier() { self.choices[index] = Some(Arc::new(choice)); self.choices_set += 1; return; } } } } 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>>>) { let old = &mut self.pokemon[index as usize]; if let Some(old_pokemon) = old { let mut p = old_pokemon.write().unwrap(); script_hook!(on_remove, p,); p.set_on_battlefield(false); } self.pokemon[index as usize] = pokemon; let pokemon = &self.pokemon[index as usize]; if let Some(pokemon_mutex) = pokemon { let mut pokemon = pokemon_mutex.write().unwrap(); pokemon.set_battle_data(self.battle.clone(), self.index); pokemon.set_on_battlefield(true); pokemon.set_battle_index(index); let battle = self.battle.upgrade().unwrap(); let battle = battle.read().unwrap(); for side in battle.sides() { if side.index() == self.index { continue; } for opponent_mutex in side.pokemon().iter().flatten() { let mut opponent = opponent_mutex.write().unwrap(); opponent.mark_opponent_as_seen(Arc::downgrade(pokemon_mutex)); pokemon.mark_opponent_as_seen(Arc::downgrade(opponent_mutex)); } } battle.event_hook().trigger(Event::Switch { side_index: self.index, index, pokemon: Some(&pokemon), }); script_hook!(on_switch_in, pokemon, &pokemon); } else { let battle = self.battle.upgrade().unwrap(); let battle = battle.read().unwrap(); battle.event_hook().trigger(Event::Switch { side_index: self.index, index, pokemon: None, }); } } pub fn is_pokemon_on_side(&self, pokemon: Arc>) -> bool { for p in self.pokemon.iter().flatten() { if p.read().unwrap().unique_identifier() == pokemon.unique_identifier() { return true; } } false } pub fn mark_slot_as_unfillable(&mut self, pokemon: &Pokemon<'a>) { for (i, slot) in self.pokemon.iter().enumerate() { if let Some(p) = slot { if p.read().unwrap().deref() as *const Pokemon == pokemon as *const Pokemon { self.fillable_slots[i] = false; return; } } } } pub fn is_slot_unfillable(&self, pokemon: Arc>) -> bool { for (i, slot) in self.pokemon.iter().enumerate() { if let Some(p) = slot { if p.read().unwrap().unique_identifier() == pokemon.unique_identifier() { return self.fillable_slots[i]; } } } false } pub fn is_defeated(&self) -> bool { for fillable_slot in &self.fillable_slots { if *fillable_slot { return false; } } true } pub fn has_fled(&self) -> bool { self.has_fled_battle } pub fn mark_as_fled(&mut self) { self.has_fled_battle = true; } pub fn get_random_creature_index(&self) -> u8 { // TODO: Consider adding parameter to only get index for available creatures. self.battle .upgrade() .unwrap() .read() .unwrap() .random() .get_max(self.pokemon_per_side as i32) as u8 } pub fn swap_positions(&mut self, a: u8, b: u8) -> bool { // If out of range, don't allow swapping. if a >= self.pokemon_per_side || b >= self.pokemon_per_side { return false; } // If the two indices are the same, don't allow swapping. if a == b { return false; } let battle = self.battle.upgrade().unwrap(); let battle = battle.read().unwrap(); // Fetch parties for the two indices. let mut party_a = None; let mut party_b = None; for party in battle.parties() { if party.is_responsible_for_index(self.index, a) { party_a = Some(party); } if party.is_responsible_for_index(self.index, b) { party_b = Some(party); } } // If either of the parties does not exist, fail. if party_a.is_none() || party_b.is_none() { return false; } // Don't allow swapping if different parties are responsible for the indices. if party_a.unwrap() as *const BattleParty != party_b.unwrap() as *const BattleParty { return false; } self.pokemon.swap(a as usize, b as usize); battle.event_hook().trigger(Event::Swap { side_index: self.index, index_a: a, index_b: b, }); true } } impl<'a> VolatileScripts<'a> for BattleSide<'a> { fn volatile_scripts(&self) -> &Arc> { &self.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.battle .upgrade() .unwrap() .read() .unwrap() .library() .load_script(crate::ScriptCategory::Side, key) } } impl<'a> ScriptSource for BattleSide<'a> { fn get_script_count(&self) { todo!() } }