wowlab_engine_content/hooks/subtlety_rogue/
techniques.rs1use 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
14const 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}