Skip to main content

wowlab_engine_combat/systems/damage_pipeline/setup/
armor.rs

1#[cfg(test)]
2use wowlab_engine_domain::rotation::DenseBuffer;
3#[cfg(test)]
4use wowlab_types::sim::EnemyIdx;
5use wowlab_types::{constants::HUNDRED, sim::ActorId};
6
7use super::DamageFlags;
8use crate::state::BuffEffect;
9#[cfg(test)]
10use crate::state::CombatState;
11
12pub(super) fn for_target(
13    view: crate::context::HookView<'_>,
14    source: ActorId,
15    flags: DamageFlags,
16) -> f64 {
17    let crate::context::HookView {
18        state, buf, target, ..
19    } = view;
20
21    let Some(target) = target else {
22        return 0.0;
23    };
24
25    if !flags.contains(DamageFlags::PHYSICAL) || flags.contains(DamageFlags::PERIODIC) {
26        return 0.0;
27    }
28
29    let armor = state.enemy_definition(target).map_or(
30        0.0,
31        wowlab_engine_domain::encounter::EnemyActorDefinition::armor,
32    );
33
34    if armor <= 0.0 {
35        tracing::warn!("creature armor is non-positive; armor mitigation skipped");
36    }
37
38    let mut bypass_pct = 0.0;
39
40    for key in buf.aura_keys() {
41        if key.source() != source || key.affected() != ActorId::Enemy(target) {
42            continue;
43        }
44
45        let Some(slot) = buf.aura(key).filter(|slot| slot.is_occupied()) else {
46            continue;
47        };
48        let Some(local) = state.aura_local_for_key(key) else {
49            continue;
50        };
51        let Some(aura) = state.defs.auras.get(local.as_usize()) else {
52            continue;
53        };
54
55        for effect in aura.effects.iter().flatten() {
56            if let BuffEffect::ArmorBypassFromCaster(percent) = effect {
57                bypass_pct += percent * f64::from(slot.stacks);
58            }
59        }
60    }
61
62    armor
63        * state.config.game_data.target_armor_mult()
64        * (1.0 - bypass_pct.clamp(0.0, HUNDRED) / HUNDRED)
65}
66
67#[cfg(test)]
68pub(super) fn for_fixture(state: &CombatState, buf: &DenseBuffer, flags: DamageFlags) -> f64 {
69    for_target(
70        crate::HookView {
71            state,
72            buf,
73            target: state.current_target(),
74            now: wowlab_types::sim::SimTime::ZERO,
75        },
76        ActorId::Player,
77        flags,
78    )
79}
80
81#[cfg(test)]
82mod tests;