Loads more work on battling, initial stretch to run a turn done.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1,24 +1,26 @@
|
||||
use crate::dynamic_data::choices::TurnChoice;
|
||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChoiceQueue {
|
||||
queue: Vec<Arc<ChoiceQueue>>,
|
||||
pub struct ChoiceQueue<'battle, 'library> {
|
||||
queue: Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
impl ChoiceQueue {
|
||||
pub fn new(queue: Vec<Arc<ChoiceQueue>>) -> Self {
|
||||
impl<'battle, 'library> ChoiceQueue<'battle, 'library> {
|
||||
pub fn new(queue: Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>>) -> Self {
|
||||
Self { queue, current: 0 }
|
||||
}
|
||||
|
||||
pub fn dequeue(&mut self) -> &Arc<ChoiceQueue> {
|
||||
pub fn dequeue<'b>(&'b mut self) -> &'b Arc<RwLock<TurnChoice<'battle, 'library>>> {
|
||||
let c = &self.queue[self.current];
|
||||
self.current += 1;
|
||||
c
|
||||
}
|
||||
|
||||
pub fn peek(&mut self) -> &Arc<ChoiceQueue> {
|
||||
pub fn peek(&mut self) -> &'battle Arc<RwLock<TurnChoice>> {
|
||||
&self.queue[self.current]
|
||||
}
|
||||
|
||||
@@ -29,4 +31,8 @@ impl ChoiceQueue {
|
||||
pub fn move_pokemon_choice_next(&mut self, _pokemon: &Pokemon) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn get_queue(&self) -> &Vec<Arc<RwLock<TurnChoice<'battle, 'library>>>> {
|
||||
&self.queue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod choice_queue;
|
||||
pub mod target_resolver;
|
||||
pub mod turn_runner;
|
||||
|
||||
@@ -5,11 +5,10 @@ use num_traits::abs;
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type TargetList<'a> = Vec<Option<Arc<RwLock<Pokemon<'a>>>>>;
|
||||
pub type TargetList<'own, 'library> = Vec<Option<Arc<RwLock<Pokemon<'own, 'library>>>>>;
|
||||
|
||||
fn get_all_targets<'b>(battle: &Battle<'b>) -> TargetList<'b> {
|
||||
let mut v =
|
||||
Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize);
|
||||
fn get_all_targets<'b, 'library>(battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> {
|
||||
let mut v = Vec::with_capacity(battle.pokemon_per_side() as usize * battle.number_of_sides() as usize);
|
||||
for side in battle.sides() {
|
||||
for pokemon in side.pokemon() {
|
||||
v.push(pokemon.as_ref().cloned());
|
||||
@@ -25,16 +24,13 @@ fn get_opposite_side(side: u8) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> {
|
||||
fn get_all_adjacent<'b, 'library>(side: u8, index: u8, battle: &Battle<'b, 'library>) -> TargetList<'b, 'library> {
|
||||
let left = index as i32 - 1;
|
||||
let right = index + 1;
|
||||
if left < 0 && right >= battle.pokemon_per_side() {
|
||||
return vec![
|
||||
battle.get_pokemon(side, index).as_ref().cloned(),
|
||||
battle
|
||||
.get_pokemon(get_opposite_side(side), index)
|
||||
.as_ref()
|
||||
.cloned(),
|
||||
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
|
||||
];
|
||||
}
|
||||
if left >= 0 {
|
||||
@@ -43,19 +39,13 @@ fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<
|
||||
battle.get_pokemon(side, index).as_ref().cloned(),
|
||||
battle.get_pokemon(side, left as u8).as_ref().cloned(),
|
||||
battle.get_pokemon(side, right).as_ref().cloned(),
|
||||
battle
|
||||
.get_pokemon(get_opposite_side(side), index)
|
||||
.as_ref()
|
||||
.cloned(),
|
||||
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
battle.get_pokemon(side, index).as_ref().cloned(),
|
||||
battle.get_pokemon(side, left as u8).as_ref().cloned(),
|
||||
battle
|
||||
.get_pokemon(get_opposite_side(side), index)
|
||||
.as_ref()
|
||||
.cloned(),
|
||||
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
|
||||
]
|
||||
};
|
||||
}
|
||||
@@ -63,14 +53,15 @@ fn get_all_adjacent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<
|
||||
vec![
|
||||
battle.get_pokemon(side, index).as_ref().cloned(),
|
||||
battle.get_pokemon(side, right).as_ref().cloned(),
|
||||
battle
|
||||
.get_pokemon(get_opposite_side(side), index)
|
||||
.as_ref()
|
||||
.cloned(),
|
||||
battle.get_pokemon(get_opposite_side(side), index).as_ref().cloned(),
|
||||
]
|
||||
}
|
||||
|
||||
fn get_all_adjacent_opponent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> TargetList<'b> {
|
||||
fn get_all_adjacent_opponent<'b, 'library>(
|
||||
side: u8,
|
||||
index: u8,
|
||||
battle: &Battle<'b, 'library>,
|
||||
) -> TargetList<'b, 'library> {
|
||||
let left = index as i32 - 1;
|
||||
let right = index + 1;
|
||||
if left < 0 && right >= battle.pokemon_per_side() {
|
||||
@@ -95,12 +86,12 @@ fn get_all_adjacent_opponent<'b>(side: u8, index: u8, battle: &Battle<'b>) -> Ta
|
||||
]
|
||||
}
|
||||
|
||||
pub fn resolve_targets<'b>(
|
||||
pub fn resolve_targets<'b, 'library>(
|
||||
side: u8,
|
||||
index: u8,
|
||||
target: MoveTarget,
|
||||
battle: &Battle<'b>,
|
||||
) -> TargetList<'b> {
|
||||
battle: &Battle<'b, 'library>,
|
||||
) -> TargetList<'b, 'library> {
|
||||
match target {
|
||||
MoveTarget::Adjacent
|
||||
| MoveTarget::AdjacentAlly
|
||||
|
||||
344
src/dynamic_data/flow/turn_runner.rs
Normal file
344
src/dynamic_data/flow/turn_runner.rs
Normal file
@@ -0,0 +1,344 @@
|
||||
use crate::dynamic_data::choices::TurnChoice;
|
||||
use crate::dynamic_data::event_hooks::event_hook::Event;
|
||||
use crate::dynamic_data::flow::target_resolver::resolve_targets;
|
||||
use crate::dynamic_data::models::battle::Battle;
|
||||
use crate::dynamic_data::models::damage_source::DamageSource;
|
||||
use crate::dynamic_data::models::executing_move::ExecutingMove;
|
||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||
use crate::dynamic_data::script_handling::{ScriptSource, ScriptWrapper};
|
||||
use crate::static_data::{DataLibrary, MoveCategory};
|
||||
use crate::{run_scripts, script_hook, script_hook_on_lock, PkmnResult};
|
||||
use parking_lot::RwLock;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Arc;
|
||||
|
||||
impl<'own, 'library> Battle<'own, 'library> {
|
||||
pub(crate) fn run_turn(&mut self) -> PkmnResult<()> {
|
||||
let cq = self.current_turn_queue();
|
||||
let mut choice_queue = cq.as_ref().unwrap().write();
|
||||
|
||||
// We are now at the very beginning of a turn. We have assigned speeds and priorities to all
|
||||
// choices, and put them in the correct order.
|
||||
|
||||
// The first thing to do is to run the on_before_turn script hook on every choice. This script hook
|
||||
// is primarily intended to be used to reset variables on a script (for example scripts that need
|
||||
// to check whether a pokemon was hit this turn. By resetting here, and setting a variable to true
|
||||
// they can then know this later on.)
|
||||
for choice in choice_queue.get_queue() {
|
||||
let choice_guard = choice.read();
|
||||
let c = choice_guard.deref();
|
||||
script_hook!(on_before_turn, c, c);
|
||||
}
|
||||
|
||||
// Now we can properly begin executing choices.
|
||||
// One by one dequeue the turns, and run them. If the battle has ended we do not want to
|
||||
// continue running however.
|
||||
while choice_queue.has_next() && !self.has_ended() {
|
||||
let choice = choice_queue.dequeue().clone();
|
||||
self.execute_choice(choice.clone())?;
|
||||
}
|
||||
|
||||
// If the battle is not ended, we have arrived at the normal end of a turn. and thus want
|
||||
// to run the end turn scripts.
|
||||
|
||||
// As we want all scripts to run exactly once, including those on the sides and battles,
|
||||
// we can't just use the default script hook macro on each pokemon. Instead manually call
|
||||
// the script functions on every script.
|
||||
if !self.has_ended() {
|
||||
let mut scripts;
|
||||
for side in self.sides() {
|
||||
for pokemon in side.pokemon().iter().flatten() {
|
||||
scripts = Vec::new();
|
||||
pokemon.read().get_own_scripts(&mut scripts);
|
||||
run_scripts!(on_end_turn, scripts,);
|
||||
}
|
||||
scripts = Vec::new();
|
||||
side.get_own_scripts(&mut scripts);
|
||||
run_scripts!(on_end_turn, scripts,);
|
||||
}
|
||||
scripts = Vec::new();
|
||||
self.get_own_scripts(&mut scripts);
|
||||
run_scripts!(on_end_turn, scripts,);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_choice(&self, choice: Arc<RwLock<TurnChoice<'own, 'library>>>) -> PkmnResult<()> {
|
||||
let choice_guard = choice.read();
|
||||
if let TurnChoice::Pass(..) = choice_guard.deref() {
|
||||
return Ok(());
|
||||
}
|
||||
if self.has_ended() {
|
||||
return Ok(());
|
||||
}
|
||||
let user = choice_guard.user().read();
|
||||
if !user.is_usable() {
|
||||
return Ok(());
|
||||
}
|
||||
if !user.is_on_battlefield() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !self.can_use(choice_guard.deref()) {
|
||||
return Ok(());
|
||||
}
|
||||
match choice_guard.deref() {
|
||||
TurnChoice::Move(..) => self.execute_move_choice(&choice)?,
|
||||
TurnChoice::Item(_) => {}
|
||||
TurnChoice::Switch(_) => {}
|
||||
TurnChoice::Flee(_) => {}
|
||||
TurnChoice::Pass(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_move_choice<'func>(
|
||||
&'func self,
|
||||
choice: &'func Arc<RwLock<TurnChoice<'own, 'library>>>,
|
||||
) -> PkmnResult<()> {
|
||||
let mut write_guard = choice.write();
|
||||
let choice = write_guard.get_move_turn_data();
|
||||
let used_move = choice.used_move();
|
||||
let move_data_lock = used_move.read();
|
||||
let mut move_data = move_data_lock.move_data();
|
||||
let mut move_name = move_data.name().clone();
|
||||
script_hook!(change_move, choice, choice, &mut move_name);
|
||||
if move_name != *move_data.name() {
|
||||
move_data = self.library().static_data().moves().get(&move_name).unwrap();
|
||||
// FIXME: also change the script on the choice.
|
||||
}
|
||||
let target_type = move_data.target();
|
||||
let targets = resolve_targets(choice.target_side(), choice.target_index(), target_type, self);
|
||||
|
||||
let mut number_of_hits: u8 = 1;
|
||||
script_hook!(change_number_of_hits, choice, choice, &mut number_of_hits);
|
||||
if number_of_hits == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut executing_move = ExecutingMove::new(
|
||||
&targets,
|
||||
number_of_hits,
|
||||
choice.user().clone(),
|
||||
used_move.clone(),
|
||||
move_data,
|
||||
choice.script().clone(),
|
||||
);
|
||||
let mut prevented = false;
|
||||
script_hook!(prevent_move, executing_move, &executing_move, &mut prevented);
|
||||
if prevented {
|
||||
return Ok(());
|
||||
}
|
||||
if !executing_move.chosen_move().write().try_use(1) {
|
||||
return Ok(());
|
||||
}
|
||||
self.event_hook().trigger(Event::MoveUse {
|
||||
executing_move: &executing_move,
|
||||
});
|
||||
let mut fail = false;
|
||||
script_hook!(fail_move, executing_move, &executing_move, &mut fail);
|
||||
if fail {
|
||||
// TODO: Add fail handling
|
||||
return Ok(());
|
||||
}
|
||||
let mut stop = false;
|
||||
script_hook!(stop_before_move, executing_move, &executing_move, &mut stop);
|
||||
if stop {
|
||||
return Ok(());
|
||||
}
|
||||
script_hook!(on_before_move, executing_move, &executing_move);
|
||||
for target in targets.iter().flatten() {
|
||||
self.handle_move_for_target(&mut executing_move, target)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_move_for_target(
|
||||
&self,
|
||||
executing_move: &mut ExecutingMove<'_, 'own, '_>,
|
||||
target: &Arc<RwLock<Pokemon<'own, '_>>>,
|
||||
) -> PkmnResult<()> {
|
||||
{
|
||||
let mut fail = false;
|
||||
script_hook_on_lock!(fail_incoming_move, target, executing_move, target, &mut fail);
|
||||
if fail {
|
||||
// TODO: Add fail handling
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
{
|
||||
let mut invulnerable = false;
|
||||
script_hook_on_lock!(is_invulnerable, target, executing_move, target, &mut invulnerable);
|
||||
if invulnerable {
|
||||
// TODO: Add fail handling
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let number_of_hits = executing_move.number_of_hits();
|
||||
if number_of_hits == 0 {
|
||||
script_hook_on_lock!(on_move_miss, target, executing_move, target);
|
||||
self.event_hook().trigger(Event::Miss {
|
||||
user: executing_move.user().read().deref(),
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let target_hit_stat = executing_move.get_index_of_target(target)?;
|
||||
for hit_index in 0..number_of_hits {
|
||||
if self.has_ended() {
|
||||
return Ok(());
|
||||
}
|
||||
if executing_move.user().read().is_fainted() {
|
||||
break;
|
||||
}
|
||||
if target.read().is_fainted() {
|
||||
break;
|
||||
}
|
||||
{
|
||||
let mut hit_type = executing_move.use_move().move_type();
|
||||
script_hook!(
|
||||
change_move_type,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
&mut hit_type
|
||||
);
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_move_type(hit_type);
|
||||
let mut effectiveness = self
|
||||
.library()
|
||||
.static_data()
|
||||
.types()
|
||||
.get_effectiveness(hit_type, target.read().types());
|
||||
script_hook!(
|
||||
change_effectiveness,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
&mut effectiveness
|
||||
);
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_effectiveness(effectiveness);
|
||||
let mut block_critical = false;
|
||||
script_hook!(
|
||||
block_critical,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
&mut block_critical
|
||||
);
|
||||
script_hook_on_lock!(
|
||||
block_incoming_critical,
|
||||
target,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
&mut block_critical
|
||||
);
|
||||
|
||||
if !block_critical {
|
||||
let is_critical =
|
||||
self.library()
|
||||
.misc_library()
|
||||
.is_critical(self, executing_move, target, hit_index);
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_critical(is_critical);
|
||||
}
|
||||
let base_power = self.library().damage_calculator().get_base_power(
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
executing_move.get_hit_data(target, hit_index)?,
|
||||
);
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_base_power(base_power);
|
||||
let damage = self.library().damage_calculator().get_damage(
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
executing_move.get_hit_data(target, hit_index)?,
|
||||
);
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_damage(damage);
|
||||
|
||||
if executing_move.use_move().category() == MoveCategory::Status {
|
||||
if executing_move.use_move().has_secondary_effect() {
|
||||
let secondary_effect_chance = executing_move.use_move().secondary_effect().chance();
|
||||
if secondary_effect_chance == -1.0
|
||||
|| self
|
||||
.random()
|
||||
.effect_chance(secondary_effect_chance, executing_move, target, hit_index)
|
||||
{
|
||||
script_hook!(on_secondary_effect, executing_move, executing_move, target, hit_index);
|
||||
// TODO: on fail
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut damage = executing_move
|
||||
.get_hit_from_raw_index(target_hit_stat + hit_index as usize)
|
||||
.damage();
|
||||
let current_health = target.read().current_health();
|
||||
if damage > current_health {
|
||||
damage = current_health;
|
||||
executing_move
|
||||
.get_hit_from_raw_index_mut(target_hit_stat + hit_index as usize)
|
||||
.set_damage(damage);
|
||||
}
|
||||
if damage > 0 {
|
||||
target.write().damage(damage, DamageSource::AttackDamage);
|
||||
if !target.read().is_fainted() {
|
||||
script_hook_on_lock!(on_incoming_hit, target, executing_move, target, hit_index);
|
||||
} else {
|
||||
script_hook!(on_opponent_faints, executing_move, executing_move, target, hit_index);
|
||||
}
|
||||
|
||||
if executing_move.use_move().has_secondary_effect() && !target.read().is_fainted() {
|
||||
let mut prevent_secondary = false;
|
||||
script_hook_on_lock!(
|
||||
prevent_secondary_effect,
|
||||
target,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
&mut prevent_secondary
|
||||
);
|
||||
if !prevent_secondary {
|
||||
let secondary_effect_chance = executing_move.use_move().secondary_effect().chance();
|
||||
if secondary_effect_chance == -1.0
|
||||
|| self.random().effect_chance(
|
||||
secondary_effect_chance,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index,
|
||||
)
|
||||
{
|
||||
script_hook!(
|
||||
on_secondary_effect,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_index
|
||||
);
|
||||
// TODO: on fail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !executing_move.user().read().is_fainted() {
|
||||
script_hook!(on_after_hits, executing_move, executing_move, target);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user