Skip to main content

wowlab_engine_combat/systems/procs/
accumulating.rs

1use super::{
2    CombatCtx, HookCtx, ImpactEvent, ImpactFilter, PeriodicImpactFilter, PeriodicImpactPolicy,
3    RANDOM_CONTRIBUTION_RANGE_MULTIPLIER, ScratchDisposition, impact_filter_matches,
4    proc_policy_allows, with_scratch,
5};
6
7pub(super) fn fire_accumulating_impact_procs(ctx: &mut CombatCtx<'_>, impact: ImpactEvent) {
8    let original = std::mem::take(&mut ctx.state.defs.accumulating_impact_procs);
9    let scratch = std::mem::take(&mut ctx.state.runtime.scratch.accumulating_impacts);
10    let (procs, scratch) = with_scratch(
11        original,
12        scratch,
13        ScratchDisposition::PersistChanges,
14        |entries| {
15            for proc in entries {
16                if !proc_policy_allows(impact, proc.driver)
17                    || !impact_filter_matches(
18                        ImpactFilter {
19                            actor: proc.actor_filter,
20                            spell: proc.spell_filter,
21                            periodic: PeriodicImpactFilter::from(PeriodicImpactPolicy {
22                                periodic_only: proc.periodic_only,
23                                skip_periodic: proc.skip_periodic,
24                            }),
25                            physical_only: false,
26                            crit_only: proc.crit_only,
27                        },
28                        impact,
29                    )
30                {
31                    continue;
32                }
33
34                if proc.accumulator.is_nan() {
35                    proc.accumulator = (ctx.rng)() * proc.threshold;
36                }
37
38                let contribution = (proc.contribution)(ctx.state, impact).max(0.0);
39
40                if !advance_threshold_accumulator(
41                    &mut proc.accumulator,
42                    proc.threshold,
43                    contribution,
44                    (ctx.rng)(),
45                ) {
46                    continue;
47                }
48
49                let mut hook_ctx = HookCtx::new(
50                    crate::context::HookCtxServices {
51                        state: ctx.state,
52                        buf: ctx.buf,
53                        sink: ctx.sink,
54                        rng: ctx.rng,
55                    },
56                    crate::context::HookCtxRequest::for_target(ctx.now, impact.target)
57                        .with_source(impact.source),
58                )
59                .with_driver_spell(impact.spell_id)
60                .with_source_damage_flags(crate::DamageFlags::PROC);
61
62                (proc.fire)(&mut hook_ctx, impact);
63            }
64        },
65    );
66
67    ctx.state.defs.accumulating_impact_procs = procs;
68    ctx.state.runtime.scratch.accumulating_impacts = scratch;
69}
70
71pub(super) const fn advance_threshold_accumulator(
72    accumulator: &mut f64,
73    threshold: f64,
74    contribution: f64,
75    roll: f64,
76) -> bool {
77    *accumulator += roll * contribution * RANDOM_CONTRIBUTION_RANGE_MULTIPLIER;
78
79    if *accumulator < threshold {
80        return false;
81    }
82
83    *accumulator -= threshold;
84
85    true
86}