Skip to main content

wowlab_engine_content/hooks/assassination_rogue/
poisons.rs

1use wowlab_engine_combat::DamageOps as _;
2use wowlab_engine_combat::ResourceOps as _;
3use wowlab_engine_combat::AuraOps as _;
4use wowlab_engine_combat::{DamageFlags, HookCtx};
5use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
6use wowlab_engine_rng::proc_chance;
7use wowlab_types::{constants::HUNDRED, sim::SpellIdx};
8
9use crate::generated::specs::assassination_rogue::{
10    AURA, AURA_AMPLIFYING_POISON_DEBUFF, AURA_DEADLY_POISON_DOT, AURA_DEATHMARK, AURA_ENVENOM,
11    AURA_KINGSBANE, AURA_KINGSBANE_RAMP, EFFECT, SPELL,
12};
13
14use super::config::sin_config;
15
16pub(super) fn proc_poisons(ctx: &mut HookCtx<'_>) {
17    let cfg = sin_config(ctx);
18    let envenom_bonus = if ctx.is_aura_active(AURA_ENVENOM.raw()) {
19        cfg.envenom_poison_bonus
20    } else {
21        0.0
22    };
23    let deadly_chance = ctx
24        .game_data()
25        .proc_chance_with_flat_bonus(SpellIdx::from_raw(AURA::DEADLY_POISON), envenom_bonus)
26        .unwrap_or(0.0);
27
28    if cfg.has_deadly_poison && proc_chance(ctx.rng(), deadly_chance) {
29        deal_deadly_poison(ctx);
30    }
31
32    let amplifying_chance = ctx
33        .game_data()
34        .proc_chance_with_flat_bonus(SpellIdx::from_raw(AURA::AMPLIFYING_POISON), envenom_bonus)
35        .unwrap_or(0.0);
36
37    if cfg.has_amplifying_poison && proc_chance(ctx.rng(), amplifying_chance) {
38        deal_amplifying_poison(ctx);
39    }
40}
41
42// Deathmark duplicates lethal-poison triggers while its DoT is active.
43const DEATHMARK_LETHAL_EXECUTIONS: u32 = 2;
44
45fn lethal_executions(ctx: &HookCtx<'_>) -> u32 {
46    if ctx.is_aura_active(AURA_DEATHMARK.raw()) {
47        DEATHMARK_LETHAL_EXECUTIONS
48    } else {
49        1
50    }
51}
52
53fn ramp_kingsbane(ctx: &mut HookCtx<'_>) {
54    if ctx.is_aura_active(AURA_KINGSBANE.raw()) {
55        ctx.apply_aura(AURA_KINGSBANE_RAMP.raw());
56    }
57}
58
59pub(super) fn deal_deadly_poison(ctx: &mut HookCtx<'_>) {
60    for _ in 0..lethal_executions(ctx) {
61        if ctx.is_aura_active(AURA_DEADLY_POISON_DOT.raw()) {
62            let coef = ctx
63                .game_data()
64                .effect_ap_coefficient(EFFECT::DEADLY_POISON_INSTANT_DMG);
65
66            ctx.deal_damage_ap(SPELL::DEADLY_POISON_INSTANT, coef, DamageFlags::empty());
67            dashing_scoundrel(ctx);
68        }
69
70        ctx.apply_aura(AURA_DEADLY_POISON_DOT.raw());
71        ramp_kingsbane(ctx);
72    }
73}
74
75pub(super) fn deal_amplifying_poison(ctx: &mut HookCtx<'_>) {
76    let coef = ctx
77        .game_data()
78        .effect_ap_coefficient(EFFECT::AMPLIFYING_POISON_DMG);
79
80    for _ in 0..lethal_executions(ctx) {
81        ctx.deal_damage_ap(EFFECT::AMPLIFYING_POISON_DMG.0, coef, DamageFlags::empty());
82        ctx.apply_aura(AURA_AMPLIFYING_POISON_DEBUFF.raw());
83        ramp_kingsbane(ctx);
84        dashing_scoundrel(ctx);
85    }
86}
87
88// Hook poison crits use sheet critical strike chance because hit results are unavailable.
89fn dashing_scoundrel(ctx: &mut HookCtx<'_>) {
90    let cfg = sin_config(ctx);
91
92    if cfg.dashing_scoundrel_energy <= 0.0 || !ctx.is_aura_active(AURA_ENVENOM.raw()) {
93        return;
94    }
95
96    let crit = ctx.buf().player().crit / HUNDRED;
97
98    if proc_chance(ctx.rng(), crit) {
99        ctx.gain_resource(cfg.dashing_scoundrel_energy);
100    }
101}
102
103// Seal Fate uses sheet critical strike chance because hit results are unavailable.
104pub(super) fn seal_fate(ctx: &mut HookCtx<'_>) {
105    let cfg = sin_config(ctx);
106
107    if cfg.seal_fate_chance <= 0.0 {
108        return;
109    }
110
111    let crit = ctx.buf().player().crit / HUNDRED;
112
113    if !proc_chance(ctx.rng(), crit * cfg.seal_fate_chance) {
114        return;
115    }
116
117    ctx.gain_secondary_resource(cfg.seal_fate_cp);
118
119    if cfg.deal_fate_cp > 0.0 {
120        ctx.gain_secondary_resource(cfg.deal_fate_cp);
121    }
122}