Skip to main content

wowlab_engine_content/hooks/subtlety_rogue/
techniques.rs

1use wowlab_engine_combat::DamageOps as _;
2use wowlab_engine_combat::ResourceOps as _;
3use wowlab_engine_combat::ProcOps as _;
4use wowlab_engine_combat::AuraOps as _;
5use wowlab_engine_combat::{DamageFlags, HookCtx, SwingEvent};
6use wowlab_engine_rng::proc_chance;
7
8use crate::generated::specs::subtlety_rogue::{
9    AURA_SHADOW_DANCE, AURA_SHADOW_TECHNIQUES,
10};
11
12use super::config::{SubtletyConfig, subtlety_config};
13
14/// Proc at the 4th qualifying swing, or the 3rd on a coin flip; Shadowcraft in Shadow Dance shifts both down one.
15const SHT_UPPER: i32 = 4;
16const SHT_LOWER: i32 = 3;
17const SHT_COIN_FLIP: f64 = 0.5;
18const INSTANT_POISON_CHANCE: f64 = 0.30;
19
20pub(super) fn proc_instant_poison(ctx: &mut HookCtx<'_>) {
21    if proc_chance(ctx.rng(), INSTANT_POISON_CHANCE) {
22        let coef = ctx.effect_ap_coefficient(crate::generated::specs::subtlety_rogue::DAMAGE_EFFECT::INSTANT_POISON_DAMAGE);
23
24        ctx.deal_damage_ap(crate::generated::specs::subtlety_rogue::DAMAGE_SPELL::INSTANT_POISON_DAMAGE, coef, DamageFlags::empty());
25    }
26}
27
28pub(crate) fn swing_hook(ctx: &mut HookCtx<'_>, _event: SwingEvent) {
29    proc_instant_poison(ctx);
30
31    let cfg = subtlety_config(ctx);
32    let shadowcraft_adj = i32::from(cfg.shadowcraft && ctx.is_aura_active(AURA_SHADOW_DANCE));
33
34    let counter =
35        i32::try_from(ctx.add_proc_counter(wowlab_engine_combat::generated_identities::proc_counter::SHADOW_TECHNIQUES, 1)).unwrap_or(i32::MAX);
36    let upper = SHT_UPPER - shadowcraft_adj;
37    let lower = SHT_LOWER - shadowcraft_adj;
38
39    if counter >= upper || (counter == lower && proc_chance(ctx.rng(), SHT_COIN_FLIP)) {
40        if !ctx.proc_category_ready(wowlab_engine_combat::generated_identities::proc_category::SHADOW_TECHNIQUES) {
41            return;
42        }
43
44        grant_sht_stack(ctx, &cfg);
45        ctx.reset_proc_counter(wowlab_engine_combat::generated_identities::proc_counter::SHADOW_TECHNIQUES);
46        ctx.start_proc_category_cooldown(wowlab_engine_combat::generated_identities::proc_category::SHADOW_TECHNIQUES);
47    }
48}
49
50pub(super) fn grant_sht_stack(ctx: &mut HookCtx<'_>, cfg: &SubtletyConfig) {
51    let doubled = cfg.shadowcraft && ctx.is_aura_active(AURA_SHADOW_DANCE);
52    let grants = 1 + i32::from(doubled);
53
54    ctx.apply_aura_n(AURA_SHADOW_TECHNIQUES, grants);
55    ctx.gain_resource(cfg.sht_energy * f64::from(grants));
56}
57
58pub(super) fn release_sht_stacks(ctx: &mut HookCtx<'_>) -> i32 {
59    let stacks = ctx.aura_stacks(AURA_SHADOW_TECHNIQUES);
60
61    if stacks <= 0 {
62        return 0;
63    }
64
65    let deficit = combo_point_deficit(ctx);
66    let take = stacks.min(deficit);
67
68    if take <= 0 {
69        return 0;
70    }
71
72    ctx.gain_secondary_resource(f64::from(take));
73    ctx.consume_aura_stacks(AURA_SHADOW_TECHNIQUES, take);
74
75    take
76}
77
78pub(super) fn current_combo_points(ctx: &HookCtx<'_>) -> f64 {
79    ctx.secondary_resource()
80}
81
82fn combo_point_deficit(ctx: &HookCtx<'_>) -> i32 {
83    let max = ctx.program().base_stats().secondary_resource_max;
84
85    wowlab_types::numeric::f64_to_i32_saturating_trunc((max - current_combo_points(ctx)).max(0.0))
86}