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_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
15const 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}