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; use crate::dynamic_data::script_handling::{ScriptSource, ScriptSourceData, ScriptWrapper}; use crate::{PkmnResult, ScriptCategory, StringKey}; use parking_lot::RwLock; use std::sync::Arc; #[derive(Debug)] pub struct Battle<'a> { library: &'a DynamicLibrary<'a>, parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, sides: Vec>, random: BattleRandom, current_turn_queue: Option, has_ended: bool, result: BattleResult, event_hook: EventHook, history_holder: Box, current_turn: u32, volatile_scripts: Arc>, last_turn_time: i64, script_source_data: RwLock, } impl<'a> Battle<'a> { pub fn new( library: &'a DynamicLibrary<'a>, parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, random_seed: Option, ) -> Arc> { let random = if let Some(seed) = random_seed { BattleRandom::new_with_seed(seed) } else { BattleRandom::default() }; let sides = Vec::with_capacity(number_of_sides as usize); let battle = Arc::new(RwLock::new(Self { library, parties, can_flee, number_of_sides, pokemon_per_side, sides, random, current_turn_queue: None, has_ended: false, result: BattleResult::Inconclusive, event_hook: Default::default(), history_holder: Box::new(HistoryHolder {}), current_turn: 0, volatile_scripts: Default::default(), last_turn_time: 0, 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 } pub fn library(&self) -> &'a DynamicLibrary<'a> { self.library } pub fn parties(&self) -> &Vec> { &self.parties } pub fn can_flee(&self) -> bool { self.can_flee } pub fn number_of_sides(&self) -> u8 { self.number_of_sides } pub fn pokemon_per_side(&self) -> u8 { self.pokemon_per_side } pub fn sides(&self) -> &Vec> { &self.sides } pub fn sides_mut(&mut self) -> &mut Vec> { &mut self.sides } pub fn random(&self) -> &BattleRandom { &self.random } pub fn has_ended(&self) -> bool { self.has_ended } pub fn result(&self) -> BattleResult { self.result } pub fn event_hook(&self) -> &EventHook { &self.event_hook } pub fn history_holder(&self) -> &Box { &self.history_holder } pub fn current_turn(&self) -> u32 { self.current_turn } pub fn last_turn_time(&self) -> i64 { self.last_turn_time } pub fn current_turn_queue(&self) -> &Option { &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() { return true; } } false } pub fn validate_battle_state(&mut self) { if self.has_ended { return; } let mut surviving_side_exists = false; let mut winning_side = None; for (side_index, side) in self.sides.iter().enumerate() { // If any side has fled, the battle end. if side.has_fled() { self.result = BattleResult::Inconclusive; self.has_ended = true; return; } // If the side is not defeated if !side.is_defeated() { // More than 1 surviving side. Battle is not ended if surviving_side_exists { return; } surviving_side_exists = true; winning_side = Some(side_index as u8); } } // Everyone died :( if !surviving_side_exists { self.result = BattleResult::Inconclusive; } // Someone survived, they won! else { self.result = BattleResult::Conclusive(winning_side.unwrap()); } 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> { fn volatile_scripts(&self) -> &Arc> { &self.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.library.load_script(ScriptCategory::Battle, key) } } impl<'a> ScriptSource<'a> for Battle<'a> { fn get_script_count(&self) -> usize { 1 } fn get_script_source_data(&self) -> &RwLock { &self.script_source_data } fn get_own_scripts(&self, scripts: &mut Vec) { scripts.push((&self.volatile_scripts).into()); } fn collect_scripts(&self, scripts: &mut Vec) { self.get_own_scripts(scripts); } }