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