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