Skip to main content

wowlab_engine_combat/state/defs/
drivers.rs

1use super::{BuffEffect, CastHookFn, HookView, LocalSpellIdx};
2
3/// Automatically casts `spell_local` after a primary-resource spend leaves the actor below `threshold`.
4#[derive(Clone, Copy, Debug, PartialEq)]
5#[must_use]
6pub(crate) struct ResourceThresholdTrigger {
7    pub resource_type: wowlab_types::combat::ResourceType,
8    pub threshold: f64,
9    pub spell_local: LocalSpellIdx,
10}
11
12/// One equipped weapon-enchant RPPM source and the aura it applies.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14#[must_use]
15pub struct WeaponEnchantBinding {
16    pub enchantment_id: u32,
17    pub buff_spell_id: u32,
18}
19
20/// A temporary weapon-imbue action and its rotation-state key.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22#[must_use]
23pub(crate) struct WeaponImbueBinding {
24    pub spell_id: u32,
25    pub key: &'static str,
26}
27
28/// Per-damage-event spell modifiers owned by a real spell or passive driver.
29#[derive(Clone, Copy, Debug)]
30#[must_use]
31pub struct DriverSpellModifier {
32    pub(crate) driver_spell_id: u32,
33    pub(crate) spells: &'static [u32],
34    pub(crate) resolve: DriverSpellModifierFn,
35}
36
37/// An always-present player effect owned by a real spell or passive driver.
38///
39/// Unlike an aura, this has no duration, stacks, dispel state, or buffer slot.
40#[derive(Clone, Copy, Debug)]
41#[must_use]
42pub struct PassiveDriverEffect {
43    pub(crate) driver_spell_id: u32,
44    pub(crate) effect: BuffEffect,
45}
46
47impl PassiveDriverEffect {
48    pub const fn new(driver_spell_id: u32, effect: BuffEffect) -> Self {
49        Self {
50            driver_spell_id,
51            effect,
52        }
53    }
54}
55
56/// Encounter-lifetime periodic work owned by a real spell or passive driver.
57///
58/// This is not an aura: it has no stacks, duration, dispel state, or buffer projection.
59#[derive(Clone, Copy, Debug)]
60#[must_use]
61pub struct PeriodicDriver {
62    pub(crate) driver_spell_id: u32,
63    pub(crate) tick_interval_ms: std::num::NonZeroU32,
64    pub(crate) triggered_duration_ms: Option<std::num::NonZeroU32>,
65    pub(crate) hasted_ticks: bool,
66    pub(crate) tick: CastHookFn,
67}
68
69impl PeriodicDriver {
70    /// Create an encounter-lifetime periodic driver.
71    ///
72    /// # Panics
73    ///
74    /// Panics when `tick_interval_ms` is zero.
75    pub const fn new(driver_spell_id: u32, tick_interval_ms: u32, tick: CastHookFn) -> Self {
76        let Some(tick_interval_ms) = std::num::NonZeroU32::new(tick_interval_ms) else {
77            panic!("periodic driver tick interval must be non-zero");
78        };
79
80        Self {
81            driver_spell_id,
82            tick_interval_ms,
83            triggered_duration_ms: None,
84            hasted_ticks: false,
85            tick,
86        }
87    }
88
89    pub const fn hasted(mut self) -> Self {
90        self.hasted_ticks = true;
91
92        self
93    }
94
95    /// Run only after explicit activation, stopping when the resolved duration elapses.
96    ///
97    /// # Panics
98    ///
99    /// Panics when `duration_ms` is zero.
100    pub const fn triggered_for(mut self, duration_ms: u32) -> Self {
101        let Some(duration_ms) = std::num::NonZeroU32::new(duration_ms) else {
102            panic!("triggered periodic driver duration must be non-zero");
103        };
104
105        self.triggered_duration_ms = Some(duration_ms);
106
107        self
108    }
109}
110
111pub(crate) type DriverSpellModifierFn = fn(HookView<'_>) -> DriverSpellModifierResult;
112
113/// Identity-based adjustments returned by a [`DriverSpellModifier`].
114#[derive(Clone, Copy, Debug, PartialEq)]
115#[must_use]
116pub struct DriverSpellModifierResult {
117    pub damage_mult: f64,
118    pub target_damage_mult: f64,
119    pub crit_chance_pct: f64,
120    pub crit_damage_mult: f64,
121}
122
123impl DriverSpellModifierResult {
124    pub const fn damage_mult(damage_mult: f64) -> Self {
125        Self {
126            damage_mult,
127            target_damage_mult: 1.0,
128            crit_chance_pct: 0.0,
129            crit_damage_mult: 1.0,
130        }
131    }
132
133    pub const fn crit_chance(crit_chance_pct: f64) -> Self {
134        Self {
135            damage_mult: 1.0,
136            target_damage_mult: 1.0,
137            crit_chance_pct,
138            crit_damage_mult: 1.0,
139        }
140    }
141}
142
143impl Default for DriverSpellModifierResult {
144    fn default() -> Self {
145        Self {
146            damage_mult: 1.0,
147            target_damage_mult: 1.0,
148            crit_chance_pct: 0.0,
149            crit_damage_mult: 1.0,
150        }
151    }
152}
153
154impl DriverSpellModifier {
155    pub const fn new(
156        driver_spell_id: u32,
157        spells: &'static [u32],
158        resolve: DriverSpellModifierFn,
159    ) -> Self {
160        Self {
161            driver_spell_id,
162            spells,
163            resolve,
164        }
165    }
166}