PkmnLib_rs/src/dynamic_data/models/battle.rs

464 lines
16 KiB
Rust
Executable File

use std::ffi::c_void;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Weak};
use anyhow::Result;
use anyhow_ext::anyhow;
use atomig::Atomic;
use parking_lot::RwLock;
use crate::dynamic_data::choices::TurnChoice;
use crate::dynamic_data::event_hooks::{EventData, 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::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::{ChoiceQueue, ScriptContainer};
use crate::dynamic_data::{ScriptCategory, ScriptSource, ScriptSourceData};
use crate::{script_hook, PkmnError, StringKey, VecExt};
/// The data of a battle.
#[derive(Debug)]
struct BattleData {
/// The library the battle uses for handling.
library: Arc<dyn DynamicLibrary>,
/// A list of all different parties in the battle.
parties: Vec<Arc<BattleParty>>,
/// 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<BattleSide>,
/// The RNG used for the battle.
random: Arc<BattleRandom>,
/// A queue of the yet to be executed choices in a turn.
current_turn_queue: RwLock<Option<Arc<ChoiceQueue>>>,
/// Whether or not the battle has ended.
has_ended: AtomicBool,
/// The eventual result of the battle. Inconclusive until the battle is ended.
result: RwLock<BattleResult>,
/// The handler to send all events to.
event_hook: EventHook,
/// The index of the current turn. 0 until all choices
current_turn: AtomicU32,
/// The current weather of the battle.
weather: ScriptContainer,
/// All the volatile scripts attached to a Pokemon
volatile_scripts: Arc<ScriptSet>,
/// The time the last turn took to run. Defaults to 0.
last_turn_time: Atomic<u64>,
/// Data required for this script to be a script source.
script_source_data: RwLock<ScriptSourceData>,
}
/// A pokemon battle, with any amount of sides and pokemon per side.
#[derive(Clone, Debug)]
pub struct Battle {
/// The actual data of the battle.
data: Arc<BattleData>,
}
/// A weak reference to a battle.
#[derive(Clone, Debug, Default)]
pub struct WeakBattleReference {
/// A weak reference to the actual data of the battle.
data: Weak<BattleData>,
}
impl Battle {
/// Initializes a new battle.
pub fn new(
library: Arc<dyn DynamicLibrary>,
parties: Vec<Arc<BattleParty>>,
can_flee: bool,
number_of_sides: u8,
pokemon_per_side: u8,
random_seed: Option<u128>,
) -> 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 battle = BattleData {
library,
parties,
can_flee,
number_of_sides,
pokemon_per_side,
sides,
random: Arc::new(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),
weather: Default::default(),
volatile_scripts: Default::default(),
last_turn_time: Default::default(),
script_source_data: Default::default(),
};
let battle_arc = Arc::new(battle);
let battle = Self { data: battle_arc };
for side in &battle.data.sides {
side.set_battle(battle.weak());
}
battle
}
/// The library the battle uses for handling.
pub fn library(&self) -> &Arc<dyn DynamicLibrary> {
&self.data.library
}
/// A list of all different parties in the battle.
pub fn parties(&self) -> &Vec<Arc<BattleParty>> {
&self.data.parties
}
/// Whether or not Pokemon can flee from the battle.
pub fn can_flee(&self) -> bool {
self.data.can_flee
}
/// The number of sides in the battle. Typically 2.
pub fn number_of_sides(&self) -> u8 {
self.data.number_of_sides
}
/// The number of Pokemon that can be on each side.
pub fn pokemon_per_side(&self) -> u8 {
self.data.pokemon_per_side
}
/// A list of all sides in the battle.
pub fn sides(&self) -> &Vec<BattleSide> {
&self.data.sides
}
/// The RNG used for the battle.
pub fn random(&self) -> &Arc<BattleRandom> {
&self.data.random
}
/// Whether or not the battle has ended.
pub fn has_ended(&self) -> bool {
self.data.has_ended.load(Ordering::Relaxed)
}
/// The eventual result of the battle. Inconclusive until the battle is ended.
pub fn result(&self) -> BattleResult {
*self.data.result.read()
}
/// The handler to send all events to.
pub fn event_hook(&self) -> &EventHook {
&self.data.event_hook
}
/// The index of the current turn. 0 until all choices
pub fn current_turn(&self) -> u32 {
self.data.current_turn.load(Ordering::Relaxed)
}
/// The time in nanoseconds the last turn took to run. Defaults to 0.
pub fn last_turn_time(&self) -> u64 {
self.data.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<Option<Arc<ChoiceQueue>>> {
&self.data.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<Pokemon> {
let side = self.data.sides.get(side as usize);
let pokemon_read_lock = side?.pokemon();
let pokemon = pokemon_read_lock.get(index as usize);
pokemon?.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.data.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) -> Result<()> {
// If we've already ended, we dont need to run this.
if self.has_ended() {
return Ok(());
}
let mut surviving_side_exists = false;
let mut winning_side = None;
for (side_index, side) in self.data.sides.iter().enumerate() {
// If any side has fled, the battle end.
if side.has_fled_battle() {
let mut w = self.data.result.write();
*w = BattleResult::Inconclusive;
self.data.has_ended.store(true, Ordering::SeqCst);
return Ok(());
}
// If the side is not defeated
if !side.is_defeated() {
// More than 1 surviving side. Battle is not ended
if surviving_side_exists {
return Ok(());
}
surviving_side_exists = true;
winning_side = Some(side_index as u8);
}
}
// Everyone died :(
if !surviving_side_exists {
let mut w = self.data.result.write();
*w = BattleResult::Inconclusive;
}
// Someone survived, they won!
else {
let mut w = self.data.result.write();
*w = BattleResult::Conclusive(winning_side.ok_or(anyhow!("Winning side was not set"))?);
}
self.data.has_ended.store(true, Ordering::SeqCst);
Ok(())
}
/// 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(),
) {
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(&self, choice: Arc<TurnChoice>) -> Result<bool> {
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();
match side {
Some(side) => {
self.data.sides.get_res(side as usize)?.set_choice(choice)?;
self.check_choices_set_and_run()?;
Ok(true)
}
None => Ok(false),
}
}
/// 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) -> Result<()> {
for side in &self.data.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.data.number_of_sides as usize * self.data.pokemon_per_side as usize);
for side in &self.data.sides {
let mut side_choices = side.choices().write();
for choice_opt in side_choices.deref_mut() {
let choice = choice_opt
.as_mut()
.ok_or(anyhow!("Choice was none, but all choices were set? Logic error."))?;
if let TurnChoice::Move(data) = choice.clone().deref() {
let mut change_priority = data.priority();
script_hook!(change_priority, choice, choice, &mut change_priority);
if change_priority != data.priority() {
if let TurnChoice::Move(data) = choice.clone().deref() {
data.set_priority(change_priority);
}
}
}
let mut speed = choice.user().boosted_stats().speed();
let c = choice.deref();
script_hook!(change_speed, c, c, &mut speed);
choice.set_speed(speed);
choice.set_random_value(self.data.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.data.current_turn.fetch_add(1, Ordering::SeqCst);
self.data
.current_turn_queue
.write()
.replace(Arc::new(ChoiceQueue::new(choices)));
{
self.run_turn()?;
}
self.data.current_turn_queue.write().take();
self.data.event_hook.trigger(EventData::EndTurn, Default::default());
let end_time = chrono::Utc::now();
let time = end_time - start_time;
match time.num_nanoseconds() {
None => {}
Some(v) => {
self.data.last_turn_time.store(v as u64, Ordering::SeqCst);
}
}
Ok(())
}
/// Sets the current weather for the battle. If None is passed, this clears the weather.
pub fn set_weather(&self, weather: Option<StringKey>) -> Result<()> {
if let Some(weather) = weather {
let script = self
.library()
.load_script(self.into(), ScriptCategory::Weather, &weather)?
.ok_or(anyhow!("Couldn't find weather script by name {}", weather))?;
self.data.weather.set(script);
} else {
self.data.weather.clear();
}
Ok(())
}
/// Gets the current weather of the battle. If no weather is present, this returns None.
pub fn weather_name(&self) -> Result<Option<StringKey>> {
if let Some(script) = self.data.weather.get() {
let lock = script.read();
Ok(Some(
lock.as_ref().ok_or(PkmnError::UnableToAcquireLock)?.name()?.clone(),
))
} else {
Ok(None)
}
}
/// Returns a weak reference to the battle.
pub fn weak(&self) -> WeakBattleReference {
WeakBattleReference {
data: Arc::downgrade(&self.data),
}
}
/// Gets the inner pointer to the reference counted data.
pub fn as_ptr(&self) -> *const c_void {
Arc::as_ptr(&self.data) as *const c_void
}
}
impl PartialEq for Battle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.data, &other.data)
}
}
impl WeakBattleReference {
/// Attempts to upgrade the weak reference to a strong reference. If the strong reference has
/// been dropped, this returns None.
pub fn upgrade(&self) -> Option<Battle> {
self.data.upgrade().map(|battle| Battle { data: battle })
}
/// Gets the inner pointer to the reference counted data.
pub(crate) fn as_ptr(&self) -> *const c_void {
self.data.as_ptr() as *const c_void
}
}
unsafe impl Send for WeakBattleReference {}
unsafe impl Sync for WeakBattleReference {}
impl PartialEq for WeakBattleReference {
fn eq(&self, other: &Self) -> bool {
self.data.ptr_eq(&other.data)
}
}
impl Eq for WeakBattleReference {}
impl VolatileScriptsOwner for Battle {
fn volatile_scripts(&self) -> &Arc<ScriptSet> {
&self.data.volatile_scripts
}
fn load_volatile_script(&self, key: &StringKey) -> Result<Option<Arc<dyn Script>>> {
self.data.library.load_script(self.into(), ScriptCategory::Battle, key)
}
}
impl ScriptSource for Battle {
fn get_script_count(&self) -> Result<usize> {
Ok(1)
}
fn get_script_source_data(&self) -> &RwLock<ScriptSourceData> {
&self.data.script_source_data
}
fn get_own_scripts(&self, scripts: &mut Vec<ScriptWrapper>) {
scripts.push((&self.data.weather).into());
scripts.push((&self.data.volatile_scripts).into());
}
fn collect_scripts(&self, scripts: &mut Vec<ScriptWrapper>) -> Result<()> {
self.get_own_scripts(scripts);
Ok(())
}
}
/// The result of a battle.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
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),
}
impl BattleResult {
/// Whether or not the battle has a winner.
pub fn is_conclusive(&self) -> bool {
matches!(self, Self::Conclusive(_))
}
}