Loads more work on battling, initial stretch to run a turn done.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-06-16 17:59:33 +02:00
parent a33369afcc
commit ff541b0696
50 changed files with 105871 additions and 497 deletions

View File

@@ -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 {

View 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
}
}

View File

@@ -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()),
}
}
}

View 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,
))
}
}

View File

@@ -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;