From b45d04b8721009621a859cabef1b2430c4f8fef6 Mon Sep 17 00:00:00 2001 From: TotallyNot <44345987+TotallyNot@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:59:18 +0100 Subject: [PATCH] added log! macro --- src/lib.rs | 5 +- src/log.rs | 166 +++++++++++++++------------ src/player/mod.rs | 268 ++++---------------------------------------- src/weapon/bonus.rs | 12 ++ src/weapon/mod.rs | 211 +--------------------------------- 5 files changed, 140 insertions(+), 522 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c1a3243..04bf5fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,8 +180,11 @@ impl Simulation { #[cfg(feature = "json")] pub fn read_log(&self) -> serde_json::Value { + use entity_registry::EntityRegistry; + let log = self.0.world.resource::(); - log.to_value() + let registry = self.0.world.resource::(); + log.to_value(registry) } } diff --git a/src/log.rs b/src/log.rs index 7c3a210..51d01d9 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,20 +1,18 @@ -use std::{collections::HashMap, sync::Mutex}; +use std::sync::Mutex; use bevy_ecs::{prelude::*, system::SystemParam}; use macros::LogMessage; use crate::{ + entity_registry::EntityRegistry, hierarchy::Children, - player::{ - stats::{ - AdditiveBonus, BaselineStat, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, - MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective, - SimpleStatMarker, Speed, StatMarker, StatType, Strength, WeaponAccuracy, - }, - Player, + player::stats::{ + AdditiveBonus, BaselineStat, CritRate, DamageBonus, Defence, Dexterity, EffectiveStat, + MultiplicativeBonus, SimpleStatBaseline, SimpleStatBonus, SimpleStatEffective, + SimpleStatMarker, Speed, StatMarker, StatType, Strength, WeaponAccuracy, }, - weapon::{Weapon, WeaponVerb}, - Name, Stages, + weapon::WeaponVerb, + Stages, }; #[derive(Resource)] @@ -49,6 +47,7 @@ pub enum LogValue<'a> { Unsigned(u32), Bool(bool), String(String), + Entity(Entity), OptionNone, Display(&'a (dyn std::fmt::Display + Send + Sync)), Debug(&'a (dyn std::fmt::Debug + Send + Sync)), @@ -86,6 +85,12 @@ impl From for LogValue<'static> { } } +impl From for LogValue<'static> { + fn from(value: Entity) -> Self { + Self::Entity(value) + } +} + impl<'a, T> From> for LogValue<'a> where T: Into>, @@ -100,11 +105,7 @@ where #[cfg(feature = "json")] impl<'a> LogValue<'a> { - fn to_value( - &self, - player_registry: &HashMap, - weapon_registry: &HashMap, - ) -> serde_json::Value { + fn to_value(&self, entity_registry: &EntityRegistry) -> serde_json::Value { match self { LogValue::OptionNone => serde_json::Value::Null, LogValue::Float(val) => { @@ -115,27 +116,20 @@ impl<'a> LogValue<'a> { LogValue::Unsigned(val) => serde_json::Value::Number(serde_json::Number::from(*val)), LogValue::Debug(boxed) => serde_json::Value::String(format!("{boxed:?}")), LogValue::Display(boxed) => serde_json::Value::String(format!("{boxed}")), - LogValue::Player(id) => serde_json::json!({ - "type": "player", - "name": player_registry.get(id).unwrap().name, - }), - LogValue::Weapon(id) => serde_json::json!({ - "type": "weapon", - "name": weapon_registry.get(id).unwrap().name, - }), + LogValue::Player(id) => { + serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap() + } + LogValue::Weapon(id) => { + serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap() + } + LogValue::Entity(id) => { + serde_json::to_value(entity_registry.0.get(id).unwrap()).unwrap() + } } } } pub trait LogMessage: Send + Sync + 'static { - fn torn_style( - &self, - _player_registry: HashMap, - _weapon_registry: HashMap, - ) -> Option { - None - } - fn tag(&self) -> &'static str; fn entries(&self) -> Vec<(&'static str, LogValue<'_>)>; @@ -143,9 +137,6 @@ pub trait LogMessage: Send + Sync + 'static { #[derive(Resource, Default)] pub struct Log { - pub player_registry: HashMap, - pub weapon_registry: HashMap, - pub entries: Vec>, pub expanded: bool, @@ -153,7 +144,7 @@ pub struct Log { impl Log { #[cfg(feature = "json")] - pub fn to_value(&self) -> serde_json::Value { + pub fn to_value(&self, entity_registry: &EntityRegistry) -> serde_json::Value { use serde_json::json; serde_json::json!({ @@ -161,7 +152,7 @@ impl Log { json!({ "type": e.tag(), "values": serde_json::Value::Object( - e.entries().iter().map(|e| (e.0.to_owned(), e.1.to_value(&self.player_registry, &self.weapon_registry))).collect() + e.entries().iter().map(|e| (e.0.to_owned(), e.1.to_value(entity_registry))).collect() ) }) ).collect::>() @@ -190,11 +181,8 @@ impl std::fmt::Display for Log { LogValue::OptionNone => write!(f, "None")?, LogValue::Display(val) => write!(f, "\"{val}\"")?, LogValue::Debug(val) => write!(f, "\"{val:?}\"")?, - LogValue::Player(id) => { - write!(f, "\"{}\"", self.player_registry.get(&id).unwrap().name)? - } - LogValue::Weapon(id) => { - write!(f, "\"{}\"", self.weapon_registry.get(&id).unwrap().name)? + LogValue::Player(id) | LogValue::Weapon(id) | LogValue::Entity(id) => { + write!(f, "{:?}", id)? } }; @@ -418,31 +406,6 @@ fn logging_enabled(logging: Res) -> bool { logging.0 } -fn register_entities( - player_q: Query<(Entity, &Name), With>, - weapon_q: Query<(Entity, &Name, &WeaponVerb), With>, - mut log: ResMut, -) { - for (player, name) in player_q.iter() { - log.player_registry.insert( - player, - PlayerInfo { - name: name.0.clone(), - }, - ); - } - - for (weapon, name, verb) in weapon_q.iter() { - log.weapon_registry.insert( - weapon, - WeaponInfo { - name: name.0.clone(), - verb: *verb, - }, - ); - } -} - fn append_log_messages(mut events: EventReader, mut log: ResMut) { for event in events.read() { log.entries.push(event.0.lock().unwrap().take().unwrap()); @@ -541,13 +504,59 @@ fn log_simple_stat_changes( } } +pub struct DynamicLogMessage { + pub label: &'static str, + pub entries: Vec<(&'static str, LogValue<'static>)>, +} + +impl LogMessage for DynamicLogMessage { + fn tag(&self) -> &'static str { + self.label + } + + fn entries(&self) -> Vec<(&'static str, LogValue<'_>)> { + self.entries.clone() + } +} + +#[macro_export] +macro_rules! log { + ($logger:expr, $tag:literal, { $($fields:tt)* }) => { + $logger.log(|| $crate::log_message!($tag, { $($fields)* })) + }; +} + +#[macro_export] +macro_rules! log_message { + ($tag:literal, { $($fields:tt)* }) => { + $crate::log::DynamicLogMessage { label: $tag, entries: $crate::log_values!($($fields)*) } + }; +} + +#[macro_export] +macro_rules! log_values { + (@ { $(,)* $($out:expr),* $(,)* } $(,)*) => { + vec![$($out),*] + }; + (@ { $(,)* $($out:expr),* } $label:ident: $val:expr, $($rest:tt)*) => { + $crate::log_values!(@ { $($out),*, (stringify!($label),$val.into()) } $($rest)*) + }; + (@ { $(,)* $($out:expr),* } $label:ident: ?$val:expr, $($rest:tt)*) => { + $crate::log_values!(@ { $($out),*, (stringify!($label),$crate::log::LogValue::String(format!("{:?}",$val))) } $($rest)*) + }; + (@ { $(,)* $($out:expr),* } $label:ident: %$val:expr, $($rest:tt)*) => { + $crate::log_values!(@ { $($out),*, (stringify!($label),$crate::log::LogValue::String(format!("{}",$val))) } $($rest)*) + }; + + ($($args:tt)* ) => { + $crate::log_values!(@ { } $($args)*,) + }; +} + pub(crate) fn configure(stages: &mut Stages) { stages.world.insert_resource(Log::default()); stages.world.insert_resource(Logging(false)); stages.add_event::(); - stages - .equip - .add_systems(register_entities.run_if(logging_enabled)); stages.post_turn.add_systems(append_log_messages); stages.turn.add_systems( @@ -563,3 +572,22 @@ pub(crate) fn configure(stages: &mut Stages) { .run_if(logging_enabled), ); } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn log_keys() { + let message = log_message!("test", { foo: 12u32, bar: ?17 }); + + assert_eq!(message.label, "test"); + let mut entries = message.entries.into_iter(); + + let next = entries.next(); + assert!(matches!(next, Some(("foo", LogValue::Unsigned(12))))); + + let next = entries.next(); + assert!(matches!(next, Some(("bar", LogValue::String(val))) if val == "17")); + } +} diff --git a/src/player/mod.rs b/src/player/mod.rs index 85da508..05022e9 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -1,5 +1,4 @@ use bevy_ecs::prelude::*; -use macros::LogMessage; use rand::Rng as _; use strum::Display; @@ -7,6 +6,7 @@ use crate::{ armour, effect::Effects, hierarchy::Children, + log, log::Logger, metrics::Metrics, passives::{Education, FactionUpgrades, Merits}, @@ -165,18 +165,6 @@ impl std::fmt::Display for BodyPart { } } -#[derive(Event)] -pub struct InitiateHit { - pub body_part: BodyPart, - pub weapon: Entity, - pub rounds: Option, - pub dmg: f32, - pub dmg_bonus_weapon: f32, - pub dmg_bonus_player: f32, - pub hit_chance: f32, - pub crit_rate: u16, -} - #[derive(Clone, Copy, PartialEq, Eq, Display)] pub enum FightEndType { Victory, @@ -184,16 +172,6 @@ pub enum FightEndType { Loss, } -#[derive(LogMessage)] -struct FightEnd { - #[log(player)] - actor: Entity, - #[log(player)] - recipient: Entity, - #[log(display)] - fight_end_type: FightEndType, -} - #[derive(Bundle)] pub struct PlayerBundle { pub name: Name, @@ -338,164 +316,6 @@ impl FromWorld for DamageSpread { } } -fn receive_hit( - (mut rng, spread): (ResMut, Local), - mut hit_init_events: EventReader, - current_q: Query< - ( - Entity, - &Education, - Option<&Attacker>, - &EffectiveStat, - ), - (With, With), - >, - mut target_q: Query< - ( - Entity, - &mut SimpleStatEffective, - &armour::ArmourBodyParts, - &EffectiveStat, - ), - With, - >, - armour_q: Query<&armour::ArmourBodyPart>, - (mut commands, mut logger): (Commands, Logger), - metrics: Res, -) { - #[derive(LogMessage)] - struct HitTarget { - #[log(player)] - actor: Entity, - #[log(player)] - recipient: Entity, - #[log(weapon)] - weapon: Entity, - - #[log(display)] - part: BodyPart, - part_mult: f32, - rounds: Option, - - dmg: u32, - health_before: u16, - health_after: u16, - - dmg_intrinsic: f32, - dmg_spread: f32, - - armour_mitigation: f32, - def_mitigation: f32, - - weapon_dmg: f32, - bonus_dmg: f32, - hit_chance: f32, - - crit_rate: u16, - } - - if hit_init_events.is_empty() { - return; - } - - let (target, mut health, body_parts, target_def) = target_q.single_mut(); - let (current, edu, attacker, current_str) = current_q.single(); - - let def_str_ratio = (target_def.value / current_str.value).clamp(1.0 / 32.0, 14.0); - let def_mitigation = if def_str_ratio < 1.0 { - 0.5 * def_str_ratio.log(32.0) + 0.5 - } else { - 0.5 * def_str_ratio.log(14.0) + 0.5 - }; - - let dmg_intrinsic = 7.0 * (current_str.value / 10.0).log10().powi(2) - + 27.0 * (current_str.value / 10.0).log10() - + 30.0; - - for event in hit_init_events.read() { - let mult = match event.body_part { - BodyPart::Head | BodyPart::Heart | BodyPart::Throat => { - metrics.increment_counter(current, "crit", 1); - metrics.increment_counter(event.weapon, "crit", 1); - 1.0 - } - BodyPart::LeftHand | BodyPart::RightHand | BodyPart::LeftFoot | BodyPart::RightFoot => { - 0.2 - } - BodyPart::LeftArm | BodyPart::RightArm | BodyPart::LeftLeg | BodyPart::RightLeg => { - 1.0 / 3.5 - } - BodyPart::Groin | BodyPart::Stomach | BodyPart::Chest => 1.0 / 1.75, - }; - - metrics.increment_counter(current, "hit", 1); - metrics.increment_counter(event.weapon, "hit", 1); - - let armour_parts = armour_q.get(body_parts.0[event.body_part.into()]).unwrap(); - let piece = rng.sample(armour_parts); - let armour_mitigation = piece.map_or(0.0, |p| p.armour_value); - - // NOTE: The beta distribution is defined on [0,1], so we rescale here - let dmg_spread = rng.sample(spread.0) / 10.0 + 1.0; - - let mut dmg_bonus = event.dmg_bonus_weapon + event.dmg_bonus_player; - - if edu.bio2380 && event.body_part == BodyPart::Throat { - dmg_bonus += 0.10; - } - - let dmg = dmg_intrinsic - * event.dmg - * dmg_bonus - * (1.0 - armour_mitigation) - * (1.0 - def_mitigation) - * mult - * dmg_spread; - let dmg = dmg.round() as u32; - - metrics.record_histogram(current, "dmg", dmg); - metrics.record_histogram(event.weapon, "dmg", dmg); - - let health_before = health.value; - - health.value = health.value.saturating_sub(dmg as u16); - - logger.log(|| HitTarget { - actor: current, - recipient: target, - weapon: event.weapon, - part: event.body_part, - part_mult: mult, - rounds: event.rounds, - dmg, - health_before, - health_after: health.value, - dmg_spread, - dmg_intrinsic, - armour_mitigation, - def_mitigation, - weapon_dmg: event.dmg, - bonus_dmg: dmg_bonus, - hit_chance: event.hit_chance, - crit_rate: event.crit_rate, - }); - - if health.value == 0 { - commands.entity(target).insert(Defeated); - logger.log(|| FightEnd { - actor: current, - recipient: target, - fight_end_type: if attacker.is_some() { - FightEndType::Victory - } else { - FightEndType::Loss - }, - }); - metrics.increment_counter(current, "victory", 1); - } - } -} - // NOTE: unfortunately this function can't really be split into smaller parts due to the existence // of multi turn bonuses #[allow(clippy::too_many_arguments)] @@ -554,48 +374,6 @@ pub fn use_damaging_weapon( Res, ), ) { - #[derive(LogMessage)] - pub struct MissTarget { - #[log(player)] - pub actor: Entity, - #[log(player)] - pub recipient: Entity, - #[log(weapon)] - pub weapon: Entity, - pub rounds: Option, - } - - #[derive(LogMessage)] - struct HitTarget { - #[log(player)] - actor: Entity, - #[log(player)] - recipient: Entity, - #[log(weapon)] - weapon: Entity, - - #[log(display)] - part: BodyPart, - part_mult: f32, - rounds: Option, - - dmg: u32, - health_before: u16, - health_after: u16, - - dmg_intrinsic: f32, - dmg_spread: f32, - - armour_mitigation: f32, - def_mitigation: f32, - - weapon_dmg: f32, - bonus_dmg: f32, - hit_chance: f32, - - crit_rate: u16, - } - let Ok((weapon, w_dmg, acc, dmg_bonus, crit, children, non_targeted)) = weapon_q.get_single() else { return; @@ -674,11 +452,11 @@ pub fn use_damaging_weapon( }; if hit_chance <= 1.0 && !rng.gen_bool(hit_chance as f64) { - logger.log(|| MissTarget { - weapon, + log!(logger, "miss_target", { + weapon: weapon, actor: player, recipient: target, - rounds, + rounds: rounds, }); metrics.increment_counter(player, "miss", 1); metrics.increment_counter(weapon, "miss", 1); @@ -745,7 +523,7 @@ pub fn use_damaging_weapon( DamageProcEffect::MultiTurn { value, bonus } if multi_attack_proc.is_none() => { - if rng.gen_bool(*value as f64) { + if rng.gen_bool((*value / 100.0) as f64) { match bonus { MultiTurnBonus::Blindfire => { multi_attack_proc = Some(MultiAttack::Blindfire) @@ -763,6 +541,8 @@ pub fn use_damaging_weapon( Some(MultiAttack::DoubleTap { first_shot: true }) } }; + metrics.increment_counter(player, bonus.counter_label(), 1); + metrics.increment_counter(weapon, bonus.counter_label(), 1); } } _ => (), @@ -774,23 +554,22 @@ pub fn use_damaging_weapon( health.value = health.value.saturating_sub(dmg as u16); - logger.log(|| HitTarget { + log!(logger, "hit_target", { actor: player, recipient: target, - weapon, - part: body_part, + weapon: weapon, + part: %body_part, part_mult: mult, - rounds, - dmg, - health_before, + dmg: dmg, + health_before: health_before, health_after: health.value, - dmg_spread, - dmg_intrinsic, - armour_mitigation, - def_mitigation, + dmg_spread: dmg_spread, + dmg_intrinsic: dmg_intrinsic, + armour_mitigation: armour_mitigation, + def_mitigation: def_mitigation, weapon_dmg: w_dmg.0, bonus_dmg: dmg_bonus.value, - hit_chance, + hit_chance: hit_chance, crit_rate: crit.value, }); @@ -798,14 +577,14 @@ pub fn use_damaging_weapon( defeated = true; commands.entity(target).insert(Defeated); - logger.log(|| FightEnd { + log!(logger, "fight_end", { actor: player, recipient: target, - fight_end_type: if attacker { + fight_end_type: %if attacker { FightEndType::Victory } else { FightEndType::Loss - }, + } }); metrics.increment_counter(player, "victory", 1); } @@ -846,12 +625,12 @@ pub fn check_stalemate( if *state == FightStatus::Ongoing && current_turns.0 >= 25 && attacker.is_some() { commands.entity(current).insert(Defeated); let target = target_q.single(); - logger.log(|| FightEnd { + + log!(logger, "fight_end", { actor: current, recipient: target, - fight_end_type: FightEndType::Stalemate, + fight_end_type: %FightEndType::Stalemate }); - metrics.increment_counter(current, "stalemate", 1); if other_attackers_q.is_empty() { @@ -898,7 +677,6 @@ pub(crate) fn configure(stages: &mut Stages) { status_effect::configure(stages); stages.add_event::(); - stages.add_event::(); stages.equip.add_systems(designate_first); stages.pre_fight.add_systems(derive_max_health); stages.pre_turn.add_systems(pick_action); diff --git a/src/weapon/bonus.rs b/src/weapon/bonus.rs index 70b054f..86f0896 100644 --- a/src/weapon/bonus.rs +++ b/src/weapon/bonus.rs @@ -143,6 +143,18 @@ pub enum MultiTurnBonus { Rage, } +impl MultiTurnBonus { + #[inline(always)] + pub fn counter_label(self) -> &'static str { + match self { + Self::Blindfire => "proc_blindfire", + Self::Fury => "proc_fury", + Self::DoubleTap => "proc_double_tap", + Self::Rage => "proc_rage", + } + } +} + pub(crate) fn prepare_bonuses( bonus_q: Query<( &Parent, diff --git a/src/weapon/mod.rs b/src/weapon/mod.rs index b67d4a6..261e49e 100644 --- a/src/weapon/mod.rs +++ b/src/weapon/mod.rs @@ -1,28 +1,22 @@ use bevy_ecs::prelude::*; use macros::LogMessage; -use rand::Rng as _; use crate::{ effect::{Effects, TurnLimitedEffect}, hierarchy::{HierarchyBuilder, Parent}, log::Logger, - metrics::Metrics, passives::{Education, FactionUpgrades, Merits}, player::{ stats::{ AdditiveBonus, AmmoControl, ClipSize, Clips, CritRate, DamageBonus, Dexterity, - EffectiveStat, SimpleStatBonus, SimpleStatBundle, SimpleStatEffective, Speed, - WeaponAccuracy, + SimpleStatBonus, SimpleStatBundle, SimpleStatEffective, WeaponAccuracy, }, - BodyPart, Current, CurrentTarget, InitiateHit, Player, Weapons, + Current, Weapons, }, - Id, Name, Rng, Stages, + Id, Name, Stages, }; -use self::{ - bonus::{FirstTurnBonus, MultiTurnBonus, TurnTriggeredBonus}, - temp::{NonTargeted, Uses}, -}; +use self::bonus::{FirstTurnBonus, MultiTurnBonus, TurnTriggeredBonus}; pub mod bonus; pub mod temp; @@ -685,203 +679,6 @@ fn unset_current(weapon_q: Query, With)>, mut com } } -// TODO: Move all mitigation aspects of this into the player system -pub fn use_damaging_weapon( - mut rng: ResMut, - weapon_q: Query< - ( - Entity, - &DamageStat, - &SimpleStatEffective, - &SimpleStatEffective, - &SimpleStatEffective, - Has, - ), - (With, With, Without), - >, - player_q: Query< - ( - Entity, - &EffectiveStat, - &SimpleStatEffective, - &SimpleStatEffective, - &SimpleStatEffective, - ), - (With, With), - >, - target_q: Query<(Entity, &EffectiveStat), With>, - (mut ammo_q, mut temp_q): ( - Query<( - &mut Ammo, - &SimpleStatEffective, - &RateOfFire, - &SimpleStatEffective, - )>, - Query<&mut Uses>, - ), - mut hit_events: EventWriter, - (mut logger, mut commands, metrics): (Logger, Commands, Res), -) { - let Ok((weapon, dmg, acc, dmg_bonus, crit, non_targeted)) = weapon_q.get_single() else { - return; - }; - let (player, player_spd, player_crit, acc_bonus, p_dmg_bonus) = player_q.single(); - let (target, target_dex) = target_q.single(); - - if let Ok(mut uses) = temp_q.get_mut(weapon) { - uses.0 -= 1; - if uses.0 == 0 { - commands.entity(weapon).remove::(); - } - } - - let spd_dex_ratio = (player_spd.value / target_dex.value).clamp(1.0 / 64.0, 64.0); - let base_hit_chance = if spd_dex_ratio < 1.0 { - 0.5 * (8.0 * spd_dex_ratio.sqrt() - 1.0) / 7.0 - } else { - 1.0 - 0.5 * (8.0 / spd_dex_ratio.sqrt() - 1.0) / 7.0 - }; - - let mut acc_eff = acc + acc_bonus; - - let mut ammo = ammo_q - .get_mut(weapon) - .ok() - .map(|(ammo, clips, rof, ammo_ctrl)| { - let ammo_ctrl = 1.0 - (ammo_ctrl).value; - let rof_eff = ((rof.0[0] as f32) * ammo_ctrl)..((rof.0[1] as f32) * ammo_ctrl); - (ammo, clips, rof_eff) - }); - - enum MultiAttack { - Blindfire, - Rage(usize), - Fury(usize), - DoubleTap { fired_first: bool }, - } - - let mut multi_attack_proc = None; - let crit = player_crit + crit; - - loop { - let rounds = ammo.as_mut().map(|(ref mut ammo, clips, rof)| { - let rounds = (rng.gen_range(rof.clone()).round() as u16).clamp(1, ammo.0); - metrics.increment_counter(player, "rounds_fired", rounds.into()); - metrics.increment_counter(weapon, "rounds_fired", rounds.into()); - ammo.0 -= rounds; - if ammo.0 == 0 { - if clips.value == 0 { - commands.entity(weapon).remove::(); - } else { - commands.entity(weapon).insert(NeedsReload); - } - } - rounds - }); - - let hit_chance = if base_hit_chance < 0.5 { - base_hit_chance + acc_eff.value * base_hit_chance - } else { - base_hit_chance + acc_eff.value * (1.0 - base_hit_chance) - }; - - if hit_chance <= 1.0 && !rng.gen_bool(hit_chance as f64) { - logger.log(|| MissTarget { - weapon, - actor: player, - recipient: target, - rounds, - }); - metrics.increment_counter(player, "miss", 1); - metrics.increment_counter(weapon, "miss", 1); - - if multi_attack_proc.is_none() { - return; - }; - } else { - let body_part = if !non_targeted { - rng.sample(crit) - } else { - BodyPart::Stomach - }; - - hit_events.send(InitiateHit { - body_part, - weapon, - rounds, - dmg: dmg.0, - dmg_bonus_weapon: dmg_bonus.value, - dmg_bonus_player: p_dmg_bonus.value, - hit_chance, - crit_rate: crit.value, - }); - } - - match multi_attack_proc { - Some(MultiAttack::Blindfire) => acc_eff.value -= 5.0 / 50.0, - Some(MultiAttack::Rage(turns @ 1..)) => { - multi_attack_proc = Some(MultiAttack::Rage(turns - 1)) - } - Some(MultiAttack::Fury(turns @ 1..)) => { - multi_attack_proc = Some(MultiAttack::Fury(turns - 1)) - } - Some(MultiAttack::DoubleTap { fired_first: false }) => { - multi_attack_proc = Some(MultiAttack::DoubleTap { fired_first: true }) - } - _ => break, - } - } - /* - let hit_chance = if base_hit_chance < 0.5 { - base_hit_chance + acc_eff.value * base_hit_chance - } else { - base_hit_chance + acc_eff.value * (1.0 - base_hit_chance) - }; - - if hit_chance <= 1.0 && !rng.gen_bool(hit_chance as f64) { - logger.log(|| MissTarget { - weapon, - actor: player, - recipient: target, - rounds: rounds.map(|(rounds, _)| rounds), - }); - metrics.increment_counter(player, "miss", 1); - metrics.increment_counter(weapon, "miss", 1); - return; - } - - let crit = player_crit + crit; - - let body_part = if !non_targeted { - rng.sample(crit) - } else { - BodyPart::Stomach - }; - - let def_str_ratio = (target_def.value / player_str.value).clamp(1.0 / 32.0, 14.0); - let mitigation = if def_str_ratio < 1.0 { - 0.5 * def_str_ratio.log(32.0) + 0.5 - } else { - 0.5 * def_str_ratio.log(14.0) + 0.5 - }; - - let dmg_intrinsic = 7.0 * (player_str.value / 10.0).log10().powi(2) - + 27.0 * (player_str.value / 10.0).log10() - + 30.0; - - init_hit.send(InitiateHit { - body_part, - weapon, - rounds: rounds.map(|(rounds, _)| rounds), - crit_rate: crit.value, - dmg: dmg.0, - dmg_bonus_weapon: (dmg_bonus + p_dmg_bonus).value, - dmg_intrinsic, - def_mitigation: mitigation, - hit_chance, - }); */ -} - fn reload_weapon( mut weapon_q: Query< (