1use super::{
2 CdrEffect, CritChargeReduction, DEFAULT_GCD_MS, DamageEffectRef, HookCtx, LocalAuraIdx,
3 LocalSpellIdx, MAX_CDR_EFFECTS, PetActionBar, RuntimeDamageDef, SpellIdx, SwingHookFn,
4 SwingMissChanceHookFn, SwingReplacementHookFn,
5};
6use crate::state::AuraState;
7
8pub type CastHookFn = fn(&mut HookCtx<'_>);
9
10pub(crate) type TriggerProgramPredicateFn = fn(&HookCtx<'_>) -> bool;
12
13pub const MAX_AURA_APPLICATION_EXCLUSIONS: usize = 5;
15
16#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
20#[must_use]
21pub struct EmpowerReleaseAdjustment {
22 pub(crate) periodic_tick_transfer: Option<EmpowerPeriodicTickTransfer>,
23}
24
25impl EmpowerReleaseAdjustment {
26 pub const fn new() -> Self {
27 Self {
28 periodic_tick_transfer: None,
29 }
30 }
31
32 pub const fn transfer_periodic_ticks(total_ticks: u16, transferred_ticks: u16) -> Self {
34 Self {
35 periodic_tick_transfer: Some(EmpowerPeriodicTickTransfer {
36 total_ticks,
37 transferred_ticks,
38 }),
39 }
40 }
41}
42
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub(crate) struct EmpowerPeriodicTickTransfer {
45 pub(crate) total_ticks: u16,
46 pub(crate) transferred_ticks: u16,
47}
48
49pub(crate) type EmpowerReleaseHookFn = fn(&HookCtx<'_>, rank: u8) -> EmpowerReleaseAdjustment;
51
52#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
54pub struct SpellEffectRange {
55 pub start: u32,
56 pub len: u16,
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60#[non_exhaustive]
61pub enum ResourcePool {
62 Primary,
63 Secondary,
64}
65
66#[derive(Clone, Copy, Debug)]
68#[non_exhaustive]
69pub enum SpellEffectData {
70 Conditional {
71 predicate: TriggerProgramPredicateFn,
72 effects: SpellEffectRange,
73 },
74 ApplyAura {
75 aura_local: LocalAuraIdx,
76 },
77 AddAuraStack {
78 aura_local: LocalAuraIdx,
79 },
80 RemoveAura {
81 aura_local: LocalAuraIdx,
82 },
83 InterruptCast {
84 lockout_ms: u32,
85 },
86 Dispel {
87 dispel_type: i32,
88 steal: bool,
89 },
90 Damage {
91 effect: DamageEffectRef,
92 damage: RuntimeDamageDef,
93 base_points: f64,
94 may_crit: bool,
95 attribute_flags: crate::DamageFlags,
96 requires_main_hand: bool,
97 requires_off_hand: bool,
98 equipped_item_requirement: Option<wowlab_types::data::EquippedItemRequirement>,
99 },
100 Heal {
101 effect: Option<DamageEffectRef>,
102 base: f64,
103 ap_coef: f64,
104 sp_coef: f64,
105 percent_of_max: f64,
106 may_crit: bool,
107 },
108 Energize {
109 effect: Option<DamageEffectRef>,
110 pool: ResourcePool,
111 amount: f64,
112 },
113 EnergizePercent {
114 resource_type: wowlab_types::combat::ResourceType,
115 percent: f64,
116 effect_index: u8,
117 },
118 ExtendAura {
119 aura_local: LocalAuraIdx,
120 amount_ms: u32,
121 },
122 MutateCooldown {
123 spell_local: LocalSpellIdx,
124 operation: wowlab_engine_domain::dbc::CooldownMutation,
125 },
126 Delayed {
127 delay_ms: u32,
128 profile_spell_id: u32,
129 effects: SpellEffectRange,
130 },
131}
132
133#[derive(Clone, Copy, Debug)]
135#[must_use]
136pub struct SpellCost {
137 pub resource_cost: f64,
138 pub optional_resource_cost: f64,
139 pub resource_cost_pct: wowlab_types::data::ResourceCostPercent,
140 pub maximum_resource_cost_pct: wowlab_types::data::ResourceCostPercent,
141 pub optional_resource_cost_pct: wowlab_types::data::ResourceCostPercent,
142 pub resource_gain: f64,
143 pub secondary_resource_cost: f64,
144 pub secondary_optional_resource_cost: f64,
145 pub secondary_resource_gain: f64,
146 pub health_cost: f64,
147 pub health_cost_pct: f64,
148 pub health_max_cost_pct: f64,
149 pub health_optional_cost: f64,
150 pub health_optional_cost_pct: wowlab_types::data::ResourceCostPercent,
151}
152
153#[derive(Clone, Copy, Debug)]
155#[must_use]
156pub struct SpellCooldown {
157 pub cooldown_hasted: bool,
158 pub start_recovery_category: Option<wowlab_types::data::CooldownCategoryId>,
159 pub has_cooldown: bool,
160 pub cooldown_duration_ms: u32,
161 pub cooldown_category: Option<wowlab_types::data::CooldownCategoryId>,
162 pub category_cooldown_duration_ms: u32,
163 pub charge_category: Option<wowlab_types::data::CooldownCategoryId>,
164 pub max_charges: u8,
165 pub recharge_ms: u32,
166 pub dynamic_max_charges: bool,
167 pub cdr_effects: [CdrEffect; MAX_CDR_EFFECTS],
168 pub cdr_count: u8,
169}
170
171#[derive(Clone, Copy, Debug)]
173#[must_use]
174pub struct SpellChannel {
175 pub tick_count: u8,
176 pub tick_interval_ms: u32,
177 pub tick_damage: RuntimeDamageDef,
178 pub tick_spell_id: u32,
179 pub tick_damage_alt: RuntimeDamageDef,
180 pub tick_damage_alt_spell_id: u32,
181 pub tick_damage_aura: u32,
182 pub tick_cost: f64,
183 pub tick_cost_alt: f64,
184 pub tick_cost_aura: u32,
185 pub tick_effects: SpellEffectRange,
186 pub complete_effects: SpellEffectRange,
187 pub timing: SpellChannelTiming,
188 pub behavior: SpellChannelBehavior,
189 pub completion: SpellChannelCompletion,
190}
191
192#[derive(Clone, Copy, Debug)]
193#[must_use]
194pub struct SpellChannelTiming {
195 pub tick_zero: bool,
196 pub tick_is_periodic: bool,
197 pub hasted_ticks: bool,
198}
199
200#[derive(Clone, Copy, Debug)]
201#[must_use]
202pub struct SpellChannelBehavior {
203 pub is_channel: bool,
204 pub duration_hasted: bool,
205 pub tick_may_crit: bool,
206}
207
208#[derive(Clone, Copy, Debug)]
209#[must_use]
210pub struct SpellChannelCompletion {
211 pub tick_damage_alt_may_crit: bool,
212 pub gcd_on_start: bool,
213 pub apply_lag: bool,
214}
215
216#[derive(Clone, Copy, Debug)]
218#[must_use]
219pub struct SpellTargeting {
220 pub explicit_target_mask: u32,
221 pub required_explicit_target_mask: u32,
222 pub aoe_max_targets: u8,
223 pub aoe_reduced_targets: u8,
224 pub aoe_full_targets: u8,
225 pub aoe_base_mult: f64,
226 pub chain_targeting: bool,
227 pub chain_multiplier: f64,
228 pub split: bool,
229 pub is_aoe: bool,
230}
231
232#[derive(Clone, Copy, Debug)]
234#[must_use]
235pub struct SpellRequirements {
236 pub shapeshift_required_mask: u64,
237 pub shapeshift_excluded_mask: u64,
238 pub caster_aura_spell: i32,
239 pub caster_aura_state: Option<AuraState>,
240 pub exclude_caster_aura_spell: i32,
241 pub exclude_caster_aura_state: Option<AuraState>,
242 pub target_aura_spell: i32,
243 pub target_aura_state: Option<AuraState>,
244 pub exclude_target_aura_spell: i32,
245 pub exclude_target_aura_state: Option<AuraState>,
246 pub equipped_item_requirement: Option<wowlab_types::data::EquippedItemRequirement>,
247 pub gating_aura_id: u32,
248 pub gating_aura_min_stacks: u8,
249 pub cost_bypass_aura_id: u32,
250 pub consume_aura_on_cast_id: u32,
251 pub cooldown_bypass_aura_id: u32,
252 pub execute_below_pct: f64,
253 pub instant_cast_aura_id: u32,
254 pub availability: SpellAvailability,
255 pub form: SpellFormRequirements,
256 pub equipment: SpellEquipmentRequirements,
257}
258
259#[derive(Clone, Copy, Debug)]
260#[must_use]
261pub struct SpellAvailability {
262 pub usable_while_casting: bool,
263 pub usable_while_moving: bool,
264 pub requires_unshifted: bool,
265}
266
267#[derive(Clone, Copy, Debug)]
268#[must_use]
269pub struct SpellFormRequirements {
270 pub allow_while_unshifted: bool,
271 pub requires_stealth: bool,
272}
273
274#[derive(Clone, Copy, Debug)]
275#[must_use]
276pub struct SpellEquipmentRequirements {
277 pub requires_behind_target: bool,
278 pub requires_main_hand: bool,
279 pub requires_off_hand: bool,
280}
281
282#[derive(Clone, Copy, Debug)]
284#[must_use]
285pub struct SpellOverride {
286 pub aura_id: u32,
287 pub spell_id: u32,
288 pub aura_min_stacks: u8,
289 pub shares_base_cooldown: bool,
290 pub preserves_aura: bool,
291}
292
293#[derive(Clone, Copy, Debug)]
295#[must_use]
296pub struct SpellData {
297 pub spell_id: u32,
298 pub name_idx: u16,
299 pub cast_time_ms: u32,
300 pub(crate) empower_cast_time_offset: u32,
301 pub empower_rank_count: u8,
302 pub max_empower_aura_id: u32,
303 pub gcd_haste_type: wowlab_types::combat::GcdHasteType,
304 pub gcd_ms: u32,
305 pub(crate) travel: crate::travel::SpellTravelData,
306 pub behavior: SpellBehavior,
307 pub cost: SpellCost,
308 pub damage: RuntimeDamageDef,
309 pub damage_policy: SpellDamagePolicy,
310 pub damage_attribute_flags: crate::DamageFlags,
311 pub damage_effect: DamageEffectRef,
312 pub base_points: f64,
313 pub applies_aura: Option<LocalAuraIdx>,
314 pub followup_effects: SpellEffectRange,
315 pub cooldown: SpellCooldown,
316 pub targeting: SpellTargeting,
317 pub channel: SpellChannel,
318 pub requirements: SpellRequirements,
319 pub overrides: SpellOverride,
320}
321
322#[derive(Clone, Copy, Debug)]
323#[must_use]
324pub struct SpellBehavior {
325 pub off_gcd: bool,
326 pub is_pet: bool,
327 pub breaks_stealth: bool,
328}
329
330#[derive(Clone, Copy, Debug)]
331#[must_use]
332pub struct SpellDamagePolicy {
333 pub may_crit: bool,
334 pub guaranteed_crit: bool,
335}
336impl SpellData {
337 #[inline]
339 #[must_use]
340 pub fn idx(&self) -> SpellIdx {
341 SpellIdx(self.spell_id)
342 }
343
344 #[inline]
346 #[must_use]
347 pub fn is_charged(&self) -> bool {
348 self.cooldown.max_charges > 1 || self.cooldown.dynamic_max_charges
349 }
350
351 #[inline]
353 #[must_use]
354 pub fn base_gcd_ms(&self) -> u32 {
355 if self.gcd_ms > 0 {
356 self.gcd_ms
357 } else {
358 DEFAULT_GCD_MS
359 }
360 }
361}
362
363#[derive(Clone, Copy, Debug)]
364#[expect(
365 clippy::struct_excessive_bools,
366 reason = "auto-attack behavior uses independent combat flags"
367)]
368pub struct AutoAttackData {
370 pub spell_id: u32,
371 pub swing_ms: u32,
372 pub ap_coef: f64,
373 pub is_physical: bool,
374 pub is_pet: bool,
375 pub npc_id: Option<u32>,
376 pub uses_spell_power: bool,
377 pub starts_active: bool,
378 pub on_crit_reduce_charge: Option<CritChargeReduction>,
379 pub miss_chance_hook: Option<SwingMissChanceHookFn>,
380 pub replacement_hook: Option<SwingReplacementHookFn>,
381 pub on_swing: Option<SwingHookFn>,
382 pub pet_action_bar: Option<PetActionBar>,
383}