wowlab_engine_combat/context/
aura.rs1use wowlab_types::sim::{ActorId, EnemyIdx, PetIdx, SimTime};
4
5use super::{AuraOps, HookCtx};
6use crate::{state::LocalAuraIdx, systems};
7
8impl HookCtx<'_> {
9 pub(crate) fn with_source_damage_flags(mut self, flags: systems::DamageFlags) -> Self {
10 self.source_damage_flags = flags;
11
12 self
13 }
14
15 pub(crate) fn with_effect_target(mut self, target: ActorId) -> Self {
16 self.effect_target = Some(target);
17
18 self
19 }
20
21 pub(crate) fn with_source_npc_id(mut self, npc_id: Option<u32>) -> Self {
22 self.source_npc_id = npc_id;
23
24 self
25 }
26
27 pub(super) fn source_flags(&self, flags: systems::DamageFlags) -> systems::DamageFlags {
28 flags | self.source_damage_flags
29 }
30
31 pub(super) fn resolved_pet_coefficient(
32 &self,
33 flags: systems::DamageFlags,
34 stat: wowlab_engine_domain::dbc::PetStatKind,
35 coefficient: f64,
36 ) -> f64 {
37 if !flags.contains(systems::DamageFlags::PET) {
38 return coefficient;
39 }
40
41 let totals = systems::accumulate_buffs(self.state, self.buf, None);
42 let (owner_attack_power, owner_spell_power) =
43 systems::dynamic_base_powers(self.state, self.buf, &totals);
44 let snapshot = systems::pet_stat_snapshot(self.state, self.source, self.source_npc_id);
45 let snapshot_ratio = match stat {
46 wowlab_engine_domain::dbc::PetStatKind::AttackPowerInheritance => {
47 snapshot.map_or(1.0, |snapshot| {
48 if owner_attack_power.abs() <= f64::EPSILON {
49 0.0
50 } else {
51 snapshot.attack_power / owner_attack_power
52 }
53 })
54 }
55 wowlab_engine_domain::dbc::PetStatKind::SpellPowerInheritance => {
56 snapshot.map_or(1.0, |snapshot| {
57 if owner_spell_power.abs() <= f64::EPSILON {
58 0.0
59 } else {
60 snapshot.spell_power / owner_spell_power
61 }
62 })
63 }
64 _ => 1.0,
65 };
66 let dbc_multiplier = self.source_npc_id.map_or(1.0, |npc_id| {
67 let data = &self.state.config.game_data;
68
69 match stat {
70 wowlab_engine_domain::dbc::PetStatKind::AttackPowerInheritance => {
71 data.pet_attack_power_inheritance_mult(npc_id)
72 }
73 wowlab_engine_domain::dbc::PetStatKind::SpellPowerInheritance => {
74 data.pet_spell_power_inheritance_mult(npc_id)
75 }
76 _ => 1.0,
77 }
78 });
79
80 coefficient * snapshot_ratio * dbc_multiplier
81 }
82
83 fn aura_local(&self, aura_id: u32) -> Option<LocalAuraIdx> {
84 self.state.aura_local(aura_id)
85 }
86}
87
88impl AuraOps for HookCtx<'_> {
89 fn with_resolved_target(&mut self, target: EnemyIdx, apply: impl FnOnce(&mut Self)) {
91 if !self.state.is_valid_target(target) {
92 return;
93 }
94
95 let previous = self.target.replace(target);
96
97 apply(self);
98 self.target = previous;
99 }
100
101 #[inline]
102 fn now(&self) -> SimTime {
103 self.now
104 }
105
106 fn apply_aura(&mut self, local: u8) {
107 systems::apply_aura(self, LocalAuraIdx::new(local));
108 }
109
110 fn apply_owner_aura(&mut self, local: u8) {
112 let source = std::mem::replace(&mut self.source, ActorId::Player);
113
114 systems::apply_aura(self, LocalAuraIdx::new(local));
115 self.source = source;
116 }
117
118 fn apply_aura_with_duration(&mut self, local: u8, duration_ms: u32) {
120 systems::apply_aura_with_duration(self, LocalAuraIdx::new(local), Some(duration_ms));
121 }
122
123 fn apply_aura_with_rolling_multiplier(&mut self, local: u8, multiplier: f64) {
125 systems::apply_aura_with_rolling_multiplier(self, LocalAuraIdx::new(local), multiplier);
126 }
127
128 fn apply_pet_aura_with_duration(&mut self, local: u8, duration_ms: u32) {
130 let source = std::mem::replace(&mut self.source, ActorId::Pet(PetIdx::PRIMARY));
131
132 systems::apply_aura_with_duration(self, LocalAuraIdx::new(local), Some(duration_ms));
133 self.source = source;
134 }
135
136 fn apply_aura_n(&mut self, local: u8, n: i32) {
137 for _ in 0..n.max(0) {
138 self.apply_aura(local);
139 }
140 }
141
142 fn add_aura_stack(&mut self, local: u8) {
144 systems::add_aura_stack(self, LocalAuraIdx::new(local));
145 }
146
147 fn add_owner_aura_stack(&mut self, local: u8) {
149 let source = std::mem::replace(&mut self.source, ActorId::Player);
150
151 systems::add_aura_stack(self, LocalAuraIdx::new(local));
152 self.source = source;
153 }
154
155 fn add_residual_damage(&mut self, local: u8, amount: f64) {
157 if amount <= 0.0 {
158 return;
159 }
160
161 let local = LocalAuraIdx::new(local);
162 let Some(key) = systems::aura_key_for(self.state, local, self.source, self.target) else {
163 return;
164 };
165
166 self.state
167 .runtime
168 .pools
169 .residual_damage_pools
170 .add(key, amount);
171 systems::apply_aura(self, local);
172 }
173
174 fn residual_damage_remaining(&self, local: u8) -> f64 {
175 let local = LocalAuraIdx::new(local);
176 let Some(key) = systems::aura_key_for(self.state, local, self.source, self.target) else {
177 return 0.0;
178 };
179
180 self.state
181 .runtime
182 .pools
183 .residual_damage_pools
184 .remaining(&key)
185 }
186
187 fn accumulate_damage(&mut self, local: u8, amount: f64) {
189 if amount <= 0.0 {
190 return;
191 }
192
193 let local = LocalAuraIdx::new(local);
194 let Some(key) = systems::aura_key_for(self.state, local, self.source, self.target) else {
195 return;
196 };
197
198 self.state
199 .runtime
200 .pools
201 .accumulated_damage_pools
202 .add(key, amount);
203 }
204
205 fn accumulate_owner_damage(&mut self, local: u8, amount: f64) {
207 let source = std::mem::replace(&mut self.source, ActorId::Player);
208
209 self.accumulate_damage(local, amount);
210 self.source = source;
211 }
212
213 fn take_accumulated_damage(&mut self, local: u8) -> f64 {
215 let local = LocalAuraIdx::new(local);
216 let Some(key) = systems::aura_key_for(self.state, local, self.source, self.target) else {
217 return 0.0;
218 };
219
220 self.state.runtime.pools.accumulated_damage_pools.take(&key)
221 }
222
223 fn expire_aura(&mut self, local: u8) {
224 systems::expire_aura(self, LocalAuraIdx::new(local));
225 }
226
227 fn expire_owner_aura(&mut self, local: u8) {
229 let source = std::mem::replace(&mut self.source, ActorId::Player);
230
231 systems::expire_aura(self, LocalAuraIdx::new(local));
232 self.source = source;
233 }
234
235 fn consume_aura(&mut self, local: u8) -> bool {
236 systems::consume_aura(self, LocalAuraIdx::new(local))
237 }
238
239 fn consume_aura_stack(&mut self, local: u8) -> bool {
240 systems::consume_aura_stack(self, LocalAuraIdx::new(local))
241 }
242
243 fn consume_owner_aura_stack(&mut self, local: u8) -> bool {
245 let source = std::mem::replace(&mut self.source, ActorId::Player);
246 let consumed = systems::consume_aura_stack(self, LocalAuraIdx::new(local));
247
248 self.source = source;
249
250 consumed
251 }
252
253 fn consume_aura_stacks(&mut self, local: u8, count: i32) -> i32 {
254 systems::consume_aura_stacks(self, LocalAuraIdx::new(local), count)
255 }
256
257 fn refresh_aura_snapshot_multiplier(&mut self, local: u8, multiplier: f64) -> bool {
258 systems::refresh_aura_snapshot_multiplier(self, LocalAuraIdx::new(local), multiplier)
259 }
260
261 fn extend_aura(&mut self, local: u8, ms: u32) {
263 systems::extend_aura(self, LocalAuraIdx::new(local), ms);
264 }
265
266 fn reduce_aura(&mut self, local: u8, ms: u32) {
268 systems::reduce_aura(self, LocalAuraIdx::new(local), ms);
269 }
270
271 fn is_aura_active(&self, local: u8) -> bool {
272 systems::is_aura_active(
273 &self.view().combat(),
274 LocalAuraIdx::new(local),
275 self.source,
276 self.target,
277 )
278 }
279
280 fn is_owner_aura_active(&self, local: u8) -> bool {
282 systems::is_aura_active(
283 &self.view().combat(),
284 LocalAuraIdx::new(local),
285 ActorId::Player,
286 self.target,
287 )
288 }
289
290 fn aura_stacks(&self, local: u8) -> i32 {
291 systems::aura_stacks(
292 self.state,
293 self.buf,
294 LocalAuraIdx::new(local),
295 self.source,
296 self.target,
297 )
298 }
299
300 fn aura_remaining_ms(&self, local: u8) -> Option<u32> {
304 systems::aura_remaining_ms(
305 self.state,
306 self.buf,
307 LocalAuraIdx::new(local),
308 self.source,
309 self.target,
310 self.now,
311 )
312 }
313
314 fn flat_periodic_damage_remaining(&self, local: u8) -> Option<f64> {
316 let local = LocalAuraIdx::new(local);
317 let aura = self.state.defs.auras.get(local.as_usize())?;
318 let periodic = aura.periodic?;
319
320 let amount = match periodic.effect {
321 crate::state::PeriodicKind::FlatDamage { amount, .. } => amount,
322 crate::state::PeriodicKind::RollingFlatDamage { amount, .. } => {
323 let key = systems::aura_key_for(self.state, local, self.source, self.target)?;
324
325 amount * self.state.runtime.pools.rolling_tick_mult.mult(&key)
326 }
327 _ => return None,
328 };
329
330 let key = systems::aura_key_for(self.state, local, self.source, self.target)?;
331 let slot = self.buf.aura(key)?;
332
333 if !slot.is_active(self.now.as_secs_f64()) {
334 return None;
335 }
336
337 let remaining_s = (slot.expires_at - self.now.as_secs_f64()).max(0.0);
338 let tick_interval_s = slot.tick_interval.max(f64::EPSILON);
339 let stack_multiplier = if aura.periodic_damage_scales_with_stacks {
340 f64::from(slot.stacks.max(1))
341 } else {
342 1.0
343 };
344
345 Some(amount * remaining_s / tick_interval_s * stack_multiplier)
346 }
347
348 fn aura_local_index(&self, aura_id: u32) -> Option<u8> {
349 self.aura_local(aura_id).map(|local| local.0)
350 }
351
352 fn apply_aura_id(&mut self, aura_id: u32) {
353 if let Some(local) = self.aura_local(aura_id) {
354 self.apply_aura(local.0);
355 }
356 }
357
358 fn apply_aura_id_n(&mut self, aura_id: u32, n: i32) {
359 if let Some(local) = self.aura_local(aura_id) {
360 self.apply_aura_n(local.0, n);
361 }
362 }
363
364 fn add_aura_stack_id(&mut self, aura_id: u32) {
365 if let Some(local) = self.aura_local(aura_id) {
366 self.add_aura_stack(local.0);
367 }
368 }
369
370 fn expire_aura_id(&mut self, aura_id: u32) {
371 if let Some(local) = self.aura_local(aura_id) {
372 self.expire_aura(local.0);
373 }
374 }
375
376 fn is_aura_active_id(&self, aura_id: u32) -> bool {
377 self.aura_local(aura_id)
378 .is_some_and(|local| self.is_aura_active(local.0))
379 }
380
381 fn aura_stacks_id(&self, aura_id: u32) -> i32 {
382 self.aura_local(aura_id)
383 .map_or(0, |local| self.aura_stacks(local.0))
384 }
385
386 fn flat_periodic_damage_remaining_id(&self, aura_id: u32) -> Option<f64> {
387 self.aura_local(aura_id)
388 .and_then(|local| self.flat_periodic_damage_remaining(local.0))
389 }
390
391 fn take_aura_stacks_id(&mut self, aura_id: u32) -> i32 {
392 let Some(local) = self.aura_local(aura_id) else {
393 return 0;
394 };
395
396 systems::take_aura_stacks(self, local)
397 }
398}