wowlab_engine_combat/systems/damage_pipeline/setup/
armor.rs1#[cfg(test)]
2use wowlab_types::sim::EnemyIdx;
3use wowlab_types::{constants::HUNDRED, sim::ActorId};
4
5use super::DamageFlags;
6use crate::state::BuffEffect;
7#[cfg(test)]
8use crate::state::CombatInstance;
9
10pub(super) fn for_target(
11 view: crate::context::HookView<'_>,
12 source: ActorId,
13 flags: DamageFlags,
14) -> f64 {
15 let crate::context::HookView {
16 program,
17 instance: state,
18 target,
19 ..
20 } = view;
21
22 let Some(target) = target else {
23 return 0.0;
24 };
25
26 if !flags.contains(DamageFlags::PHYSICAL) || flags.contains(DamageFlags::PERIODIC) {
27 return 0.0;
28 }
29
30 let armor = program.enemy_definition(target).map_or(
31 0.0,
32 wowlab_engine_domain::encounter::EnemyActorDefinition::armor,
33 );
34
35 if armor <= 0.0 {
36 tracing::warn!("creature armor is non-positive; armor mitigation skipped");
37 }
38
39 if !program.damage.has_armor_bypass {
40 return armor * program.damage.target_armor_mult;
41 }
42
43 let mut bypass_pct = 0.0;
44
45 crate::systems::buffs::for_each_planned_actor_aura_effect(
46 crate::context::ActorView::new(program, state, ActorId::Enemy(target)),
47 crate::state::EffectFamily::ARMOR_BYPASS,
48 |key, _local, stacks, effect| {
49 if key.source() != source {
50 return;
51 }
52
53 if let BuffEffect::ArmorBypassFromCaster(percent) = effect {
54 bypass_pct += percent * stacks;
55 }
56 },
57 );
58
59 armor * program.damage.target_armor_mult * (1.0 - bypass_pct.clamp(0.0, HUNDRED) / HUNDRED)
60}
61
62#[cfg(test)]
63pub(super) fn for_fixture(
64 program: &crate::state::CombatProgram,
65 state: &CombatInstance,
66 flags: DamageFlags,
67) -> f64 {
68 for_target(
69 crate::HookView {
70 program,
71 instance: state,
72 target: state.current_target(),
73 now: wowlab_types::sim::SimTime::ZERO,
74 },
75 ActorId::Player,
76 flags,
77 )
78}
79
80#[cfg(test)]
81mod tests;