use std::ops::{Deref, DerefMut}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::Arc; use atomig::Atomic; use parking_lot::RwLock; use crate::dynamic_data::choices::TurnChoice; use crate::dynamic_data::event_hooks::{Event, EventHook}; use crate::dynamic_data::models::battle_party::BattleParty; use crate::dynamic_data::models::battle_random::BattleRandom; use crate::dynamic_data::models::battle_side::BattleSide; use crate::dynamic_data::models::pokemon::Pokemon; use crate::dynamic_data::ChoiceQueue; use crate::dynamic_data::DynamicLibrary; use crate::dynamic_data::Script; use crate::dynamic_data::ScriptSet; use crate::dynamic_data::VolatileScriptsOwner; use crate::dynamic_data::{is_valid_target, ScriptWrapper}; use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData}; use crate::{script_hook, PkmnResult, StringKey}; /// A pokemon battle, with any amount of sides and pokemon per side. #[derive(Debug)] #[cfg_attr(feature = "wasm", derive(unique_type_id_derive::UniqueTypeId))] pub struct Battle { /// The library the battle uses for handling. library: Arc, /// A list of all different parties in the battle. parties: Vec, /// Whether or not Pokemon can flee from the battle. can_flee: bool, /// The number of sides in the battle. Typically 2. number_of_sides: u8, /// The number of Pokemon that can be on each side. pokemon_per_side: u8, /// A list of all sides in the battle. sides: Vec, /// The RNG used for the battle. random: BattleRandom, /// A queue of the yet to be executed choices in a turn. current_turn_queue: RwLock>, /// Whether or not the battle has ended. has_ended: AtomicBool, /// The eventual result of the battle. Inconclusive until the battle is ended. result: RwLock, /// The handler to send all events to. event_hook: EventHook, /// The index of the current turn. 0 until all choices current_turn: AtomicU32, /// All the volatile scripts attached to a Pokemon volatile_scripts: Arc, /// The time the last turn took to run. Defaults to 0. last_turn_time: Atomic, /// Data required for this script to be a script source. script_source_data: RwLock, } impl Battle { /// Initializes a new battle. pub fn new( library: Arc, parties: Vec, can_flee: bool, number_of_sides: u8, pokemon_per_side: u8, random_seed: Option, ) -> Self { // If no seed was passed, we use the current time as seed for the RNG, otherwise we use the // seed. 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: RwLock::new(None), has_ended: AtomicBool::new(false), result: RwLock::new(BattleResult::Inconclusive), event_hook: Default::default(), current_turn: AtomicU32::new(0), volatile_scripts: Default::default(), last_turn_time: Default::default(), script_source_data: Default::default(), }; let ptr: *mut Battle = &mut battle; for side in &mut battle.sides { side.set_battle(ptr); } battle } /// The library the battle uses for handling. pub fn library(&self) -> &Arc { &self.library } /// A list of all different parties in the battle. pub fn parties(&self) -> &Vec { &self.parties } /// Whether or not Pokemon can flee from the battle. pub fn can_flee(&self) -> bool { self.can_flee } /// The number of sides in the battle. Typically 2. pub fn number_of_sides(&self) -> u8 { self.number_of_sides } /// The number of Pokemon that can be on each side. pub fn pokemon_per_side(&self) -> u8 { self.pokemon_per_side } /// A list of all sides in the battle. pub fn sides(&self) -> &Vec { &self.sides } /// A mutable list of all sides in the battle. pub fn sides_mut(&mut self) -> &mut Vec { &mut self.sides } /// The RNG used for the battle. pub fn random(&self) -> &BattleRandom { &self.random } /// Whether or not the battle has ended. pub fn has_ended(&self) -> bool { self.has_ended.load(Ordering::Relaxed) } /// The eventual result of the battle. Inconclusive until the battle is ended. pub fn result(&self) -> BattleResult { *self.result.read() } /// The handler to send all events to. pub fn event_hook(&self) -> &EventHook { &self.event_hook } /// The index of the current turn. 0 until all choices pub fn current_turn(&self) -> u32 { self.current_turn.load(Ordering::Relaxed) } /// The time the last turn took to run. Defaults to 0. pub fn last_turn_time(&self) -> u64 { self.last_turn_time.load(Ordering::Relaxed) } /// A queue of the yet to be executed choices in a turn. pub fn current_turn_queue(&self) -> &RwLock> { &self.current_turn_queue } /// Get a Pokemon on the battlefield, on a specific side and an index on that side. pub fn get_pokemon(&self, side: u8, index: u8) -> Option> { let side = self.sides.get(side as usize); side?; let pokemon_read_lock = side.unwrap().pokemon(); let pokemon = pokemon_read_lock.get(index as usize); pokemon?; pokemon.unwrap().clone() } /// Returns whether a slot on the battlefield can still be filled. If no party is responsible /// for that slot, or a party is responsible, but has no remaining Pokemon to throw out anymore, /// this returns false. 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 } /// Validates whether the battle is still in a non-ended state. If the battle has ended, this /// properly sets who has won etc. pub fn validate_battle_state(&self) { // If we've already ended, we dont need to run this. 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_battle() { let _w = self.result.write(); unsafe { self.result.data_ptr().replace(BattleResult::Inconclusive); } self.has_ended.store(true, Ordering::SeqCst); 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 { let _w = self.result.write(); unsafe { self.result.data_ptr().replace(BattleResult::Inconclusive); } } // Someone survived, they won! else { let _w = self.result.write(); unsafe { self.result .data_ptr() .replace(BattleResult::Conclusive(winning_side.unwrap())); } } self.has_ended.store(true, Ordering::SeqCst); } /// Checks whether a choice is actually possible. 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(data) = choice { // TODO: Hook to change number of PP needed. if data.used_move().remaining_pp() < 1 { return false; } if !is_valid_target( data.target_side(), data.target_index(), data.used_move().move_data().target(), choice.user().deref(), ) { return false; } } true } /// Try and set the choice for the battle. If the choice is not valid, this returns false. pub fn try_set_choice(&mut self, choice: TurnChoice) -> PkmnResult { if !self.can_use(&choice) { return Ok(false); } if !choice.user().is_on_battlefield() { return Ok(false); } let side = choice.user().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) } /// Checks to see whether all Pokemon on the field have set their choices. If so, we then run /// the turn. fn check_choices_set_and_run(&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 &self.sides { let mut side_choices = side.choices().write(); for choice_opt in side_choices.deref_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()); } // Drop the lock guard, as we need to write into it in reset_choices. drop(side_choices); side.reset_choices(); } self.current_turn.fetch_add(1, Ordering::SeqCst); choices.sort_unstable_by(|a, b| b.cmp(a)); self.current_turn_queue.write().replace(ChoiceQueue::new(choices)); { self.run_turn()?; } self.current_turn_queue.write().take(); self.event_hook.trigger(Event::EndTurn); let end_time = chrono::Utc::now(); let time = end_time - start_time; self.last_turn_time .store(time.num_nanoseconds().unwrap() as u64, Ordering::SeqCst); Ok(()) } } impl VolatileScriptsOwner for Battle { fn volatile_scripts(&self) -> &Arc { &self.volatile_scripts } fn load_volatile_script(&self, key: &StringKey) -> PkmnResult>> { self.library.load_script(self.into(), ScriptCategory::Battle, key) } } impl ScriptSource for Battle { 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); } } /// The result of a battle. #[derive(Debug, Copy, Clone)] pub enum BattleResult { /// The battle has no winner. Either the battle has not ended, or everyone is dead, or one of /// the parties has ran away. Inconclusive, /// The battle has a winner, with the inner value being the index of the side that has won. Conclusive(u8), }