added log! macro

This commit is contained in:
TotallyNot 2024-01-01 11:59:18 +01:00
parent 86f9333aec
commit b45d04b872
5 changed files with 140 additions and 522 deletions

View file

@ -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<u16>,
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<crate::Rng>, Local<DamageSpread>),
mut hit_init_events: EventReader<InitiateHit>,
current_q: Query<
(
Entity,
&Education,
Option<&Attacker>,
&EffectiveStat<Strength>,
),
(With<Current>, With<Player>),
>,
mut target_q: Query<
(
Entity,
&mut SimpleStatEffective<Health>,
&armour::ArmourBodyParts,
&EffectiveStat<Defence>,
),
With<CurrentTarget>,
>,
armour_q: Query<&armour::ArmourBodyPart>,
(mut commands, mut logger): (Commands, Logger),
metrics: Res<Metrics>,
) {
#[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<u16>,
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<Metrics>,
),
) {
#[derive(LogMessage)]
pub struct MissTarget {
#[log(player)]
pub actor: Entity,
#[log(player)]
pub recipient: Entity,
#[log(weapon)]
pub weapon: Entity,
pub rounds: Option<u16>,
}
#[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<u16>,
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::<ChooseWeapon>();
stages.add_event::<InitiateHit>();
stages.equip.add_systems(designate_first);
stages.pre_fight.add_systems(derive_max_health);
stages.pre_turn.add_systems(pick_action);