use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::event_hook::{Event, 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::{script_hook, PkmnResult, ScriptCategory, StringKey}; use parking_lot::RwLock; use std::ops::{Deref, DerefMut}; use std::sync::Arc; #[derive(Debug)] pub struct Battle<'own, 'library> { library: &'own DynamicLibrary, 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: chrono::Duration, script_source_data: RwLock, } impl<'own, 'library> Battle<'own, 'library> { pub fn new( library: &'own DynamicLibrary, parties: Vec>, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, random_seed: Option, ) -> Self { let random = if let Some(seed) = random_seed { BattleRandom::new_with_seed(seed) } else { BattleRandom::default() }; let mut sides = Vec::with_capacity(number_of_sides as usize); for i in 0..number_of_sides { sides.push(BattleSide::new(i, pokemon_per_side)); } let mut battle = 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: chrono::Duration::zero(), script_source_data: Default::default(), }; let ptr: *mut Battle = &mut battle; for side in &mut battle.sides { side.set_battle(ptr); } battle } pub fn library(&self) -> &'own DynamicLibrary { 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) -> chrono::Duration { 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().read().is_usable() { return false; } if let TurnChoice::Move(data) = choice { // TODO: Hook to change number of PP needed. if data.used_move().read().remaining_pp() < 1 { return false; } if !is_valid_target( data.target_side(), data.target_index(), data.used_move().read().move_data().target(), choice.user().read().deref(), ) { return false; } } true } pub fn try_set_choice(&mut self, choice: TurnChoice<'own, 'library>) -> PkmnResult { if !self.can_use(&choice) { return Ok(false); } if !choice.user().read().is_on_battlefield() { return Ok(false); } let side = choice.user().read().get_battle_side_index(); if side.is_none() { return Ok(false); } self.sides[side.unwrap() as usize].set_choice(choice); self.check_choices_set_and_run()?; Ok(true) } fn check_choices_set_and_run(&mut self) -> PkmnResult<()> { for side in &self.sides { if !side.all_choices_set() { return Ok(()); } if !side.all_slots_filled() { return Ok(()); } } let start_time = chrono::Utc::now(); let mut choices = Vec::with_capacity(self.number_of_sides as usize * self.pokemon_per_side as usize); for side in &mut self.sides { for choice_opt in side.choices_mut() { if choice_opt.is_none() { panic!("Choice was none, but all choices were set? Logic error."); } let mut choice = choice_opt.as_mut().unwrap(); let c = choice.deref(); if let TurnChoice::Move(data) = c { let mut change_priority = data.priority(); script_hook!(change_priority, c, c, &mut change_priority); if change_priority != data.priority() { if let TurnChoice::Move(data) = choice.deref_mut() { *data.priority_mut() = change_priority; } } } let mut speed = choice.speed(); let c = choice.deref(); script_hook!(change_speed, c, c, &mut speed); *choice.speed_mut() = speed; choice.set_random_value(self.random.get() as u32); choices.push(choice_opt.take().unwrap()); } side.reset_choices(); } self.current_turn += 1; choices.sort_unstable_by(|a, b| b.cmp(a)); self.current_turn_queue = Some(Arc::new(RwLock::new(ChoiceQueue::new(choices)))); { self.run_turn()?; } self.current_turn_queue = None; self.event_hook.trigger(Event::EndTurn); let end_time = chrono::Utc::now(); let time = end_time - start_time; self.last_turn_time = time; Ok(()) } } impl<'own, 'library> VolatileScripts<'own> for Battle<'own, 'library> { fn volatile_scripts(&self) -> &Arc> { &self.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.library.load_script(ScriptCategory::Battle, key) } } impl<'own, 'library> ScriptSource<'own> for Battle<'own, 'library> { 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); } }