Skip to main content

wowlab_engine_domain/dbc/passives/
targeting.rs

1use wowlab_types::numeric::f64_to_i32_saturating_round;
2
3use super::{
4    ModifierFold, ModifierPropertyKind, PassiveQuery, effect_targets_spell,
5    fold_flat_pct_modifiers, modified_effect_base_points, modifier_property_kind,
6    passive_integral_value,
7};
8
9/// Returns the effective chain-target count after flat and percentage modifiers.
10#[must_use]
11pub fn passive_chain_targets(query: PassiveQuery<'_>, initial: i32) -> i32 {
12    passive_integral_value(query, initial, ModifierPropertyKind::ChainTargets)
13}
14
15fn passive_targeting_value(
16    query: PassiveQuery<'_>,
17    initial: f64,
18    property: ModifierPropertyKind,
19) -> f64 {
20    let PassiveQuery {
21        passives,
22        spell: target,
23    } = query;
24
25    fold_flat_pct_modifiers(
26        passives,
27        initial,
28        ModifierFold::FlatAndPercent,
29        |source, ranks, modifier| {
30            if modifier_property_kind(modifier) != Some(property)
31                || !effect_targets_spell(modifier, source, target)
32            {
33                return None;
34            }
35
36            Some(
37                modified_effect_base_points(
38                    PassiveQuery {
39                        passives,
40                        spell: source,
41                    },
42                    modifier.index,
43                    modifier.base_points,
44                ) * ranks,
45            )
46        },
47    )
48    .max(0.0)
49}
50
51macro_rules! passive_targeting_wrappers {
52    ($($(#[$meta:meta])* $method:ident => $property:ident),+ $(,)?) => {
53        $(
54            $(#[$meta])*
55            #[must_use]
56            pub fn $method(query: PassiveQuery<'_>, initial: f64) -> f64 {
57                passive_targeting_value(query, initial, ModifierPropertyKind::$property)
58            }
59        )+
60    };
61}
62
63passive_targeting_wrappers! {
64    /// Resolve hostile maximum range after passive property-5 modifiers.
65    passive_hostile_max_range => Range,
66    /// Resolve effect radius after passive property-6 modifiers.
67    passive_radius => Radius,
68    /// Resolve chain jump distance after passive property-35 modifiers.
69    passive_chain_target_range => ChainTargetRange,
70}
71
72/// Resolve the spell-level target cap after passive property-40 modifiers.
73#[must_use]
74pub fn passive_max_targets(query: PassiveQuery<'_>, initial: i32) -> i32 {
75    f64_to_i32_saturating_round(passive_targeting_value(
76        query,
77        f64::from(initial),
78        ModifierPropertyKind::MaxTargets,
79    ))
80}
81
82/// Returns the per-hop chain multiplier after percentage modifiers.
83#[must_use]
84pub fn passive_chain_multiplier(query: PassiveQuery<'_>, initial: f64) -> f64 {
85    let PassiveQuery {
86        passives,
87        spell: target,
88    } = query;
89    let normalized = if initial > 0.0 { initial } else { 1.0 };
90
91    fold_flat_pct_modifiers(
92        passives,
93        normalized,
94        ModifierFold::FlatAndPercent,
95        |source, ranks, modifier| {
96            if modifier_property_kind(modifier) != Some(ModifierPropertyKind::ChainMultiplier)
97                || !effect_targets_spell(modifier, source, target)
98            {
99                return None;
100            }
101
102            Some(modifier.base_points * ranks)
103        },
104    )
105    .max(0.0)
106}