wowlab_engine_content/
weapon_enchants.rs1use wowlab_engine_combat::{
4 AuraData, BuffEffect, BuiltCombatSystem, MAX_AURA_BUFF_EFFECTS, RppmTracker,
5 WeaponEnchantBinding, try_push_effect,
6};
7use wowlab_engine_gamedata::ResolvedGameData;
8use wowlab_engine_ports::{EngineError, WeaponEnchantProc, WeaponEnchantStat};
9use wowlab_types::game::RatingType;
10
11pub(crate) fn register_weapon_enchant_procs(
17 built: &mut BuiltCombatSystem,
18 procs: &[WeaponEnchantProc],
19) -> Result<(), EngineError> {
20 for proc in procs {
21 register_one(built, proc)?;
22 }
23
24 Ok(())
25}
26
27fn register_one(
28 built: &mut BuiltCombatSystem,
29 proc: &WeaponEnchantProc,
30) -> Result<(), EngineError> {
31 let tracker = RppmTracker {
32 rppm: proc.rppm,
33 last_attempt_time: 0.0,
34 last_proc_time: 0.0,
35 accumulated_blp: 0.0,
36 haste_scales: proc.haste_scales,
37 crit_scales: proc.crit_scales,
38 auto_attack_speed_scales: false,
39 blp_enabled: true,
40 };
41 let registered = built.register_weapon_enchant_binding(
42 WeaponEnchantBinding {
43 enchantment_id: proc.enchantment_id,
44 buff_spell_id: proc.buff_spell_id,
45 },
46 tracker,
47 )?;
48
49 if registered && built.state.aura_local(proc.buff_spell_id).is_none() {
50 register_buff_aura(built, proc)?;
51 }
52
53 Ok(())
54}
55
56const DEFAULT_ENCHANT_BUFF_DURATION_MS: u32 = 15_000;
57
58fn register_buff_aura(
59 built: &mut BuiltCombatSystem,
60 proc: &WeaponEnchantProc,
61) -> Result<(), EngineError> {
62 let duration_ms = if proc.buff_duration_ms == 0 {
63 tracing::warn!(
64 enchantment_id = proc.enchantment_id,
65 buff_spell_id = proc.buff_spell_id,
66 "buff spell duration NULL/0; defaulting to 15000ms (will fix once DB populated)"
67 );
68
69 DEFAULT_ENCHANT_BUFF_DURATION_MS
70 } else {
71 proc.buff_duration_ms
72 };
73
74 let mut effects: [Option<BuffEffect>; MAX_AURA_BUFF_EFFECTS] = [None; MAX_AURA_BUFF_EFFECTS];
75
76 if let Some(effect) = stat_to_buff_effect(
77 &built.state.config.game_data,
78 proc.buff_stat,
79 proc.buff_amount,
80 )? {
81 let _ = try_push_effect(&mut effects, effect);
83 } else {
84 tracing::warn!(
85 enchantment_id = proc.enchantment_id,
86 buff_spell_id = proc.buff_spell_id,
87 "buff stat not classified; aura registers inert (contributes zero stats)"
88 );
89 }
90
91 let aura = AuraData::simple_player_buff(proc.buff_spell_id, 0, duration_ms, effects);
92 let _ = built.register_aura(proc.name.clone(), aura)?;
93
94 Ok(())
95}
96
97fn stat_to_buff_effect(
98 data: &ResolvedGameData,
99 stat: WeaponEnchantStat,
100 amount: f64,
101) -> Result<Option<BuffEffect>, EngineError> {
102 if amount <= 0.0 {
103 return Ok(None);
104 }
105
106 let effect = match stat {
107 WeaponEnchantStat::Crit => Some(BuffEffect::Crit(
108 crate::raid_buffs::rating_percent_points(data, RatingType::Crit, amount)?,
109 )),
110 WeaponEnchantStat::Haste => Some(BuffEffect::Haste(
111 crate::raid_buffs::rating_percent_points(data, RatingType::Haste, amount)?,
112 )),
113 WeaponEnchantStat::Mastery => Some(BuffEffect::Mastery(
114 crate::raid_buffs::rating_percent_points(data, RatingType::Mastery, amount)?,
115 )),
116 WeaponEnchantStat::Versatility => Some(BuffEffect::Versatility(
117 crate::raid_buffs::rating_percent_points(data, RatingType::Versatility, amount)?,
118 )),
119 WeaponEnchantStat::PrimaryStat => Some(BuffEffect::PrimaryStat(amount)),
120 WeaponEnchantStat::PrimaryStatPercent => Some(BuffEffect::PrimaryStatPercent(amount)),
121 _ => None,
122 };
123
124 Ok(effect)
125}
126
127#[cfg(test)]
128mod tests;