wowlab_engine_combat/systems/procs/
accumulating.rs1use super::{
2 CombatCtx, HookCtx, ImpactEvent, ImpactFilter, PeriodicImpactFilter, PeriodicImpactPolicy,
3 RANDOM_CONTRIBUTION_RANGE_MULTIPLIER, impact_filter_matches, proc_policy_allows, with_scratch,
4};
5
6pub(super) fn fire_accumulating_impact_procs(ctx: &mut CombatCtx<'_>, impact: ImpactEvent) {
8 if ctx
9 .instance
10 .runtime
11 .procs
12 .accumulating_impact_procs_dispatching
13 {
14 return;
15 }
16
17 ctx.instance
18 .runtime
19 .procs
20 .accumulating_impact_procs_dispatching = true;
21 let scratch = std::mem::take(&mut ctx.instance.runtime.scratch.accumulating_impacts);
22 let scratch = with_scratch(&ctx.program.accumulating_impact_procs, scratch, |entries| {
23 for (index, proc) in entries.iter().enumerate() {
24 if !proc_policy_allows(impact, proc.driver)
25 || !impact_filter_matches(
26 ImpactFilter {
27 actor: proc.actor_filter,
28 spell: proc.spell_filter,
29 periodic: PeriodicImpactFilter::from(PeriodicImpactPolicy {
30 periodic_only: proc.periodic_only,
31 skip_periodic: proc.skip_periodic,
32 }),
33 physical_only: false,
34 crit_only: proc.crit_only,
35 },
36 impact,
37 )
38 {
39 continue;
40 }
41
42 let Some(accumulator) = ctx
43 .instance
44 .runtime
45 .procs
46 .accumulating_impact_accumulators
47 .get(index)
48 .copied()
49 else {
50 continue;
51 };
52 let mut accumulator = accumulator;
53
54 if accumulator.is_nan() {
55 accumulator = (ctx.rng)() * proc.threshold;
56 }
57
58 let contribution = (proc.contribution)(ctx.program, impact).max(0.0);
59
60 if !advance_threshold_accumulator(
61 &mut accumulator,
62 proc.threshold,
63 contribution,
64 (ctx.rng)(),
65 ) {
66 ctx.instance.runtime.procs.accumulating_impact_accumulators[index] = accumulator;
67 continue;
68 }
69
70 ctx.instance.runtime.procs.accumulating_impact_accumulators[index] = accumulator;
71
72 let mut hook_ctx = HookCtx::new(
73 crate::context::HookCtxServices {
74 program: ctx.program,
75 instance: ctx.instance,
76 sink: ctx.sink,
77 rng: ctx.rng,
78 },
79 crate::context::HookCtxRequest::for_target(ctx.now, impact.target)
80 .with_source(impact.source),
81 )
82 .with_driver_spell(impact.spell_id)
83 .with_source_damage_flags(crate::DamageFlags::PROC);
84
85 (proc.fire)(&mut hook_ctx, impact);
86 }
87 });
88
89 ctx.instance.runtime.scratch.accumulating_impacts = scratch;
90 ctx.instance
91 .runtime
92 .procs
93 .accumulating_impact_procs_dispatching = false;
94}
95
96pub(super) const fn advance_threshold_accumulator(
97 accumulator: &mut f64,
98 threshold: f64,
99 contribution: f64,
100 roll: f64,
101) -> bool {
102 *accumulator += roll * contribution * RANDOM_CONTRIBUTION_RANGE_MULTIPLIER;
103
104 if *accumulator < threshold {
105 return false;
106 }
107
108 *accumulator -= threshold;
109
110 true
111}