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:
@@ -37,8 +37,7 @@ impl BattleStatCalculator {
|
||||
}
|
||||
|
||||
pub fn calculate_boosted_stat(&self, pokemon: &Pokemon, stat: Statistic) -> u32 {
|
||||
(self.calculate_flat_stat(pokemon, stat) as f32
|
||||
* self.get_stat_boost_modifier(pokemon, stat)) as u32
|
||||
(self.calculate_flat_stat(pokemon, stat) as f32 * self.get_stat_boost_modifier(pokemon, stat)) as u32
|
||||
}
|
||||
|
||||
fn calculate_health_stat(&self, pokemon: &Pokemon) -> u32 {
|
||||
|
||||
275
src/dynamic_data/libraries/damage_library.rs
Normal file
275
src/dynamic_data/libraries/damage_library.rs
Normal file
@@ -0,0 +1,275 @@
|
||||
use crate::dynamic_data::models::executing_move::{ExecutingMove, HitData};
|
||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||
use crate::dynamic_data::script_handling::ScriptSource;
|
||||
use crate::static_data::{MoveCategory, Statistic};
|
||||
use crate::{script_hook, script_hook_on_lock};
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait DamageLibrary: std::fmt::Debug {
|
||||
fn has_randomness(&self) -> bool;
|
||||
fn get_damage(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> u32;
|
||||
|
||||
fn get_base_power(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> u8;
|
||||
|
||||
fn get_stat_modifier(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> f32;
|
||||
|
||||
fn get_damage_modifier(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> f32;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Gen7DamageLibrary {
|
||||
has_randomness: bool,
|
||||
}
|
||||
|
||||
impl Gen7DamageLibrary {
|
||||
pub fn new(has_randomness: bool) -> Self {
|
||||
Self { has_randomness }
|
||||
}
|
||||
}
|
||||
|
||||
impl DamageLibrary for Gen7DamageLibrary {
|
||||
fn has_randomness(&self) -> bool {
|
||||
self.has_randomness
|
||||
}
|
||||
|
||||
fn get_damage(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> u32 {
|
||||
if executing_move.use_move().category() == MoveCategory::Status {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let level_modifier = ((2.0 * executing_move.user().read().level() as f32) / 5.0).floor() + 2.0;
|
||||
let base_power = hit_data.base_power();
|
||||
let stat_modifier = self.get_stat_modifier(executing_move, target, hit_number, hit_data);
|
||||
let damage_modifier = self.get_damage_modifier(executing_move, target, hit_number, hit_data);
|
||||
|
||||
let mut float_damage = (level_modifier * base_power as f32).floor();
|
||||
float_damage = (float_damage * stat_modifier).floor();
|
||||
float_damage = (float_damage / 50.0).floor() + 2.0;
|
||||
float_damage = (float_damage * damage_modifier).floor();
|
||||
if executing_move.target_count() > 1 {
|
||||
float_damage = (float_damage * 0.75).floor();
|
||||
}
|
||||
if hit_data.is_critical() {
|
||||
let mut crit_modifier = 1.5;
|
||||
script_hook!(
|
||||
change_critical_modifier,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut crit_modifier
|
||||
);
|
||||
float_damage = (float_damage * crit_modifier).floor();
|
||||
}
|
||||
|
||||
if self.has_randomness {
|
||||
let battle = executing_move.user().read().get_battle().unwrap().upgrade().unwrap();
|
||||
let random_percentage = 85 + battle.read().random().get_between(0, 16);
|
||||
float_damage = (float_damage * (random_percentage as f32 / 100.0)).floor();
|
||||
}
|
||||
|
||||
if executing_move.user().read().types().contains(&hit_data.move_type()) {
|
||||
let mut stab_modifier = 1.5;
|
||||
script_hook!(
|
||||
change_stab_modifier,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut stab_modifier
|
||||
);
|
||||
float_damage = (float_damage * stab_modifier).floor();
|
||||
}
|
||||
|
||||
float_damage = (float_damage * hit_data.effectiveness()).floor();
|
||||
let mut damage = if float_damage <= 0.0 {
|
||||
if hit_data.effectiveness() == 0.0 {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
} else if float_damage >= u32::MAX as f32 {
|
||||
u32::MAX
|
||||
} else {
|
||||
float_damage as u32
|
||||
};
|
||||
|
||||
script_hook!(
|
||||
change_damage,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut damage
|
||||
);
|
||||
script_hook_on_lock!(
|
||||
change_incoming_damage,
|
||||
target,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut damage
|
||||
);
|
||||
damage
|
||||
}
|
||||
|
||||
fn get_base_power(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
_hit_data: &HitData,
|
||||
) -> u8 {
|
||||
if executing_move.use_move().category() == MoveCategory::Status {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut base_power = executing_move.use_move().base_power();
|
||||
script_hook!(
|
||||
change_base_power,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut base_power
|
||||
);
|
||||
base_power
|
||||
}
|
||||
|
||||
fn get_stat_modifier(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
hit_data: &HitData,
|
||||
) -> f32 {
|
||||
let mut user = executing_move.user().clone();
|
||||
script_hook!(
|
||||
change_damage_stats_user,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut user
|
||||
);
|
||||
let offensive_stat;
|
||||
let defensive_stat;
|
||||
if executing_move.use_move().category() == MoveCategory::Physical {
|
||||
offensive_stat = Statistic::Attack;
|
||||
defensive_stat = Statistic::Defense;
|
||||
} else {
|
||||
offensive_stat = Statistic::SpecialAttack;
|
||||
defensive_stat = Statistic::SpecialDefense;
|
||||
}
|
||||
let mut bypass_defensive_stat_boost =
|
||||
hit_data.is_critical() && target.read().stat_boost().get_stat(defensive_stat) > 0;
|
||||
script_hook!(
|
||||
bypass_defensive_stat_boost,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut bypass_defensive_stat_boost
|
||||
);
|
||||
let mut bypass_offensive_stat_boost =
|
||||
hit_data.is_critical() && user.read().stat_boost().get_stat(offensive_stat) > 0;
|
||||
script_hook!(
|
||||
bypass_offensive_stat_boost,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut bypass_offensive_stat_boost
|
||||
);
|
||||
|
||||
let mut defensive_stat = if bypass_defensive_stat_boost {
|
||||
target.read().flat_stats().get_stat(offensive_stat)
|
||||
} else {
|
||||
target.read().boosted_stats().get_stat(offensive_stat)
|
||||
};
|
||||
|
||||
let mut offensive_stat = if bypass_offensive_stat_boost {
|
||||
user.read().flat_stats().get_stat(offensive_stat)
|
||||
} else {
|
||||
user.read().boosted_stats().get_stat(offensive_stat)
|
||||
};
|
||||
|
||||
script_hook!(
|
||||
change_defensive_stat_value,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut defensive_stat
|
||||
);
|
||||
script_hook!(
|
||||
change_offensive_stat_value,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut offensive_stat
|
||||
);
|
||||
|
||||
let mut stat_modifier = offensive_stat as f32 / defensive_stat as f32;
|
||||
script_hook!(
|
||||
change_damage_stat_modifier,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut stat_modifier
|
||||
);
|
||||
|
||||
stat_modifier
|
||||
}
|
||||
|
||||
fn get_damage_modifier(
|
||||
&self,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
_hit_data: &HitData,
|
||||
) -> f32 {
|
||||
let mut modifier = 1.0;
|
||||
script_hook!(
|
||||
change_damage_modifier,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut modifier
|
||||
);
|
||||
modifier
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
|
||||
use crate::dynamic_data::libraries::damage_library::DamageLibrary;
|
||||
use crate::dynamic_data::libraries::misc_library::MiscLibrary;
|
||||
use crate::dynamic_data::libraries::script_resolver::ScriptCategory;
|
||||
use crate::dynamic_data::script_handling::item_script::ItemScript;
|
||||
use crate::dynamic_data::script_handling::script::Script;
|
||||
@@ -7,27 +9,30 @@ use crate::static_data::libraries::static_data::StaticData;
|
||||
use crate::{PkmnResult, StringKey};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DynamicLibrary<'a> {
|
||||
static_data: StaticData<'a>,
|
||||
pub struct DynamicLibrary {
|
||||
static_data: StaticData<'static>,
|
||||
stat_calculator: BattleStatCalculator,
|
||||
damage_calculator: Box<dyn DamageLibrary>,
|
||||
misc_library: Box<dyn MiscLibrary<'static>>,
|
||||
}
|
||||
|
||||
impl<'a> DynamicLibrary<'a> {
|
||||
pub fn static_data(&self) -> &StaticData<'a> {
|
||||
impl<'library> DynamicLibrary {
|
||||
pub fn static_data(&self) -> &StaticData<'library> {
|
||||
&self.static_data
|
||||
}
|
||||
pub fn stat_calculator(&self) -> &BattleStatCalculator {
|
||||
&self.stat_calculator
|
||||
}
|
||||
|
||||
pub fn load_script(
|
||||
&self,
|
||||
_category: ScriptCategory,
|
||||
_key: &StringKey,
|
||||
) -> PkmnResult<Option<Box<dyn Script>>> {
|
||||
todo!()
|
||||
pub fn damage_calculator(&self) -> &Box<dyn DamageLibrary> {
|
||||
&self.damage_calculator
|
||||
}
|
||||
pub fn misc_library(&self) -> &Box<dyn MiscLibrary<'static>> {
|
||||
&self.misc_library
|
||||
}
|
||||
|
||||
pub fn load_script(&self, _category: ScriptCategory, _key: &StringKey) -> PkmnResult<Option<Box<dyn Script>>> {
|
||||
todo!()
|
||||
}
|
||||
pub fn load_item_script(&self, _key: &Item) -> PkmnResult<Option<Box<dyn ItemScript>>> {
|
||||
todo!()
|
||||
}
|
||||
@@ -36,13 +41,17 @@ impl<'a> DynamicLibrary<'a> {
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use crate::dynamic_data::libraries::battle_stat_calculator::BattleStatCalculator;
|
||||
use crate::dynamic_data::libraries::damage_library::Gen7DamageLibrary;
|
||||
use crate::dynamic_data::libraries::dynamic_library::DynamicLibrary;
|
||||
use crate::dynamic_data::libraries::misc_library::Gen7MiscLibrary;
|
||||
use crate::static_data::libraries::static_data;
|
||||
|
||||
pub fn build<'a>() -> DynamicLibrary<'a> {
|
||||
pub fn build<'library>() -> DynamicLibrary {
|
||||
DynamicLibrary {
|
||||
static_data: static_data::test::build(),
|
||||
stat_calculator: BattleStatCalculator {},
|
||||
damage_calculator: Box::new(Gen7DamageLibrary::new(false)),
|
||||
misc_library: Box::new(Gen7MiscLibrary::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
126
src/dynamic_data/libraries/misc_library.rs
Normal file
126
src/dynamic_data/libraries/misc_library.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use crate::dynamic_data::choices::{MoveChoice, SwitchChoice, TurnChoice};
|
||||
use crate::dynamic_data::models::battle::Battle;
|
||||
use crate::dynamic_data::models::executing_move::ExecutingMove;
|
||||
use crate::dynamic_data::models::learned_move::{LearnedMove, MoveLearnMethod};
|
||||
use crate::dynamic_data::models::pokemon::Pokemon;
|
||||
use crate::dynamic_data::script_handling::ScriptSource;
|
||||
use crate::static_data::{MoveCategory, MoveData, MoveTarget, SecondaryEffect};
|
||||
use crate::{script_hook, StringKey};
|
||||
use hashbrown::HashSet;
|
||||
use parking_lot::RwLock;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait MiscLibrary<'library> {
|
||||
fn is_critical(
|
||||
&self,
|
||||
battle: &Battle,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
) -> bool;
|
||||
fn can_flee(&self, choice: &SwitchChoice) -> bool;
|
||||
fn replacement_move<'func>(
|
||||
&'func self,
|
||||
user: &Arc<RwLock<Pokemon<'func, 'library>>>,
|
||||
target_side: u8,
|
||||
target_index: u8,
|
||||
) -> TurnChoice<'func, 'library>;
|
||||
// TODO: can evolve from level up?
|
||||
// TODO: get time
|
||||
}
|
||||
|
||||
impl<'library> Debug for dyn MiscLibrary<'library> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("MiscLibrary")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Gen7MiscLibrary<'library> {
|
||||
struggle_data: *const MoveData,
|
||||
struggle_learned_move: Arc<RwLock<LearnedMove<'library>>>,
|
||||
}
|
||||
|
||||
impl<'library> Gen7MiscLibrary<'library> {
|
||||
pub fn new() -> Self {
|
||||
let struggle_data = Box::new(MoveData::new(
|
||||
&StringKey::new("struggle"),
|
||||
0,
|
||||
MoveCategory::Physical,
|
||||
50,
|
||||
255,
|
||||
10,
|
||||
MoveTarget::Any,
|
||||
0,
|
||||
SecondaryEffect::new(-1.0, StringKey::new("struggle"), vec![]),
|
||||
HashSet::new(),
|
||||
));
|
||||
let struggle_ptr = Box::into_raw(struggle_data);
|
||||
let struggle_learned_move = Arc::new(RwLock::new(LearnedMove::new(
|
||||
unsafe { &*struggle_ptr },
|
||||
MoveLearnMethod::Unknown,
|
||||
)));
|
||||
Self {
|
||||
struggle_data: struggle_ptr,
|
||||
struggle_learned_move,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'library> Drop for Gen7MiscLibrary<'library> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
Box::from_raw(self.struggle_data as *mut MoveData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'library> MiscLibrary<'library> for Gen7MiscLibrary<'library> {
|
||||
fn is_critical(
|
||||
&self,
|
||||
battle: &Battle,
|
||||
executing_move: &ExecutingMove,
|
||||
target: &Arc<RwLock<Pokemon>>,
|
||||
hit_number: u8,
|
||||
) -> bool {
|
||||
if executing_move.use_move().category() == MoveCategory::Status {
|
||||
return false;
|
||||
}
|
||||
let mut crit_stage = 0;
|
||||
script_hook!(
|
||||
change_critical_stage,
|
||||
executing_move,
|
||||
executing_move,
|
||||
target,
|
||||
hit_number,
|
||||
&mut crit_stage
|
||||
);
|
||||
// Crit stage is an unsigned byte, so we only care about values of 0 or higher.
|
||||
// For a critical stage of 3+ we always return true.
|
||||
match crit_stage {
|
||||
0 => battle.random().get_max(24) == 0,
|
||||
1 => battle.random().get_max(8) == 0,
|
||||
2 => battle.random().get_max(2) == 0,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn can_flee(&self, _choice: &SwitchChoice) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn replacement_move<'func>(
|
||||
&'func self,
|
||||
user: &Arc<RwLock<Pokemon<'func, 'library>>>,
|
||||
target_side: u8,
|
||||
target_index: u8,
|
||||
) -> TurnChoice<'func, 'library> {
|
||||
TurnChoice::Move(MoveChoice::new(
|
||||
user.clone(),
|
||||
self.struggle_learned_move.clone(),
|
||||
target_side,
|
||||
target_index,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod battle_stat_calculator;
|
||||
pub mod damage_library;
|
||||
pub mod dynamic_library;
|
||||
pub mod script_resolver;
|
||||
pub mod misc_library;
|
||||
|
||||
Reference in New Issue
Block a user