Skip to main content

wowlab_engine_content/hooks/marksmanship_hunter/
config.rs

1use wowlab_engine_combat::PeriodicKind;
2use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
3use wowlab_types::{constants::HUNDRED, sim::SpellIdx};
4
5use crate::{
6    generated::specs::marksmanship_hunter::{
7        AURA_EXPLOSIVE_SHOT, EFFECT, HERO, REPORTED_SPELL, SET_BONUS, SPELL_RAPID_FIRE,
8        TALENT,
9    },
10    hooks::gated,
11};
12
13crate::hooks::define_spec_config! {
14/// Talent-gated Marksmanship hook parameters.
15pub(super) struct MarksmanshipConfig {
16    black_arrow: bool { provenance: "Dark Ranger Black Arrow talent 466932.", gate: black_arrow, source: true, transform: |value| value, },
17    ba_coef: f64 { provenance: "Black Arrow 466930 e1 AP coefficient.", gate: true, source: ap(EFFECT::BLACK_ARROW_DAMAGE), transform: |value| value, },
18    ba_lower_pct: f64 { provenance: "Black Arrow 466930 e2 lower health threshold.", gate: true, source: base(EFFECT::BLACK_ARROW_LOWER_PCT), transform: |value| value / HUNDRED, },
19    ba_upper_pct: f64 { provenance: "Black Arrow 466930 e3 upper health threshold.", gate: true, source: base(EFFECT::BLACK_ARROW_UPPER_PCT), transform: |value| value / HUNDRED, },
20    ks_coef: f64 { provenance: "Kill Shot 53351 e1 AP coefficient.", gate: true, source: ap(EFFECT::KILL_SHOT_DAMAGE), transform: |value| value, },
21    ks_threshold: f64 { provenance: "Kill Shot 53351 e2 execute threshold.", gate: true, source: base(EFFECT::KILL_SHOT_THRESHOLD), transform: |value| value / HUNDRED, },
22    kill_shot: bool { provenance: "Kill Shot talent selection.", gate: params.talent_picked(TALENT::KILL_SHOT_TALENT), source: true, transform: |value| value, },
23    deathblow: bool { provenance: "Deathblow is enabled by its talent or Black Arrow.", gate: deathblow_talent || black_arrow, source: true, transform: |value| value, },
24    aimed_deathblow_chance: f64 { provenance: "Deathblow 343248 e1 plus Soul Drinker 469638 e3.", gate: deathblow_talent, source: aimed_chance, transform: |value| value, },
25    rf_deathblow_chance: f64 { provenance: "Deathblow 343248 e2 plus Soul Drinker 469638 e4.", gate: deathblow_talent, source: rf_chance, transform: |value| value, },
26    withering_fire: bool { provenance: "Withering Fire talent 466990.", gate: params.talent_picked(HERO::DARK_RANGER::SPELL::WITHERING_FIRE_TALENT), source: true, transform: |value| value, },
27    wf_count: i32 { provenance: "Withering Fire e3 plus Blighted Quiver e7 volley count.", gate: true, source: wf_count, transform: |value| value, },
28    wf_arrow_coef: f64 { provenance: "Withering Fire arrow AP coefficient.", gate: true, source: ap(EFFECT::WITHERING_FIRE_ARROW), transform: |value| value, },
29    unload: bool { provenance: "Unload 1277548 selection.", gate: params.talent_picked(TALENT::UNLOAD), source: true, transform: |value| value, },
30    no_scope: bool { provenance: "No Scope 473385 selection.", gate: params.talent_picked(TALENT::NO_SCOPE), source: true, transform: |value| value, },
31    precise_shots: bool { provenance: "Precise Shots 260240 selection.", gate: params.talent_picked(TALENT::PRECISE_SHOTS), source: true, transform: |value| value, },
32    arcane_coef: f64 { provenance: "Arcane Shot 185358 e1 AP coefficient.", gate: true, source: ap(EFFECT::ARCANE_SHOT_DAMAGE), transform: |value| value, },
33    bleak_arrows: bool { provenance: "Bleak Arrows 467749 selection.", gate: params.talent_picked(HERO::DARK_RANGER::SPELL::BLEAK_ARROWS_TALENT), source: true, transform: |value| value, },
34    headshot: bool { provenance: "Headshot 471363 selection.", gate: params.talent_picked(TALENT::HEADSHOT), source: true, transform: |value| value, },
35    focused_aim_cdr_ms: u32 { provenance: "Focused Aim 378767 e1 cooldown reduction milliseconds.", gate: params.talent_picked(TALENT::FOCUSED_AIM), source: base(EFFECT::FOCUSED_AIM_CDR_MS), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc, },
36    windrunner_quiver_chance: f64 { provenance: "Windrunner Quiver 473523 e3 retrigger chance.", gate: params.talent_picked(TALENT::WINDRUNNER_QUIVER), source: base(EFFECT::WINDRUNNER_QUIVER_PCT), transform: |value| value / HUNDRED, },
37    spotters_mark_chance: f64 { provenance: "Spotter's Mark 1219616 e1 chance.", gate: true, source: base(EFFECT::SPOTTERS_MARK_CHANCE), transform: |value| value / HUNDRED, },
38    spotters_mark_mult: f64 { provenance: "Spotter's Mark 466872 e1 Aimed Shot multiplier.", gate: true, source: base(EFFECT::SPOTTERS_MARK_PCT), transform: |value| 1.0 + value / HUNDRED, },
39    feathered_frenzy_mult: f64 { provenance: "Feathered Frenzy 470943 e1 mark-chance multiplier.", gate: params.talent_picked(TALENT::FEATHERED_FRENZY), source: base(EFFECT::FEATHERED_FRENZY_PCT), transform: |value| 1.0 + value / HUNDRED, fallback: 1.0, },
40    aimed_coef: f64 { provenance: "Aimed Shot 19434 e1 AP coefficient.", gate: true, source: ap(EFFECT::AIMED_SHOT_DAMAGE), transform: |value| value, },
41    master_marksman: bool { provenance: "Master Marksman 260309 selection.", gate: params.talent_picked(TALENT::MASTER_MARKSMAN), source: true, transform: |value| value, },
42    master_marksman_fraction: f64 { provenance: "Master Marksman banked damage fraction.", gate: true, source: base(EFFECT::MASTER_MARKSMAN_PCT), transform: |value| value / HUNDRED, },
43    corpsecaller: bool { provenance: "Corpsecaller 1264289 RPPM-rolls a Dark Minion on Black Arrow `DoT` ticks.", gate: params.talent_picked(HERO::DARK_RANGER::SPELL::CORPSECALLER_TALENT), source: true, transform: |value| value, },
44    wailing_dead: bool { provenance: "Wailing Dead 1264290 makes Trueshot summon a Dark Minion.", gate: params.talent_picked(HERO::DARK_RANGER::SPELL::WAILING_DEAD_TALENT), source: true, transform: |value| value, },
45    pact_of_the_hollow: bool { provenance: "Pact of the Hollow 1264690 makes Aimed Shot command every Dark Minion to fire Blighted Arrow.", gate: params.talent_picked(HERO::DARK_RANGER::SPELL::PACT_OF_THE_HOLLOW_TALENT), source: true, transform: |value| value, },
46    dark_minion_duration_ms: u32 { provenance: "Dark Minion summon 1264345 supplies the duration.", gate: true, source: dark_minion_duration_ms, transform: |value| value, },
47    dark_minion_shoot_cast_ms: u32 { provenance: "Dark Minion Shoot 1264357 supplies the cast time; the pet inherits no haste.", gate: true, source: dark_minion_shoot_cast_ms, transform: |value| value, },
48    explosive_shot_cleave_mult: f64 { provenance: "Explosive Shot 212431 e3 cleave fraction.", gate: params.talent_picked(TALENT::EXPLOSIVE_SHOT), source: base(EFFECT::EXPLOSIVE_SHOT_CLEAVE_PCT), transform: |value| 1.0 - value / HUNDRED },
49    explosive_shot_reduced_targets: usize { provenance: "Explosive Shot 212431 e2 reduced-AoE target threshold.", gate: params.talent_picked(TALENT::EXPLOSIVE_SHOT), source: base(EFFECT::EXPLOSIVE_SHOT_REDUCED_TARGETS), transform: wowlab_types::numeric::f64_to_usize_saturating_trunc },
50    mid2_explosive_tick_cdr_ms: u32 { provenance: "MID2 Marksmanship four-piece cooldown reduction per Explosive Shot tick.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_MM_4PC), source: base(EFFECT::MID2_MM_4PC_CDR_MS), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc },
51    mid2_explosive_damage_mult: f64 { provenance: "MID2 Marksmanship two-piece Explosive Shot damage bonus.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_MM_2PC), source: base(EFFECT::MID2_MM_2PC_DAMAGE), transform: |value| 1.0 + value / HUNDRED, fallback: 1.0 },
52    mid2_aimed_damage_mult: f64 { provenance: "MID2 Marksmanship four-piece Aimed Shot and Rapid Fire damage bonus.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_MM_4PC), source: base(EFFECT::MID2_MM_4PC_DAMAGE), transform: |value| 1.0 + value / HUNDRED, fallback: 1.0 },
53}
54accessor pub(super) fn marksmanship_config(ctx);
55register pub(super) fn derive_config(built, params) -> MarksmanshipConfig;
56prepare {
57    let data = &params.game_data;
58    let base = |e: (u32, u8)| data.base_points(SpellIdx::from_raw(e.0), e.1);
59    let ap = |effect| data.effect_ap_coefficient(effect);
60
61    let black_arrow = params.talent_picked(HERO::DARK_RANGER::SPELL::BLACK_ARROW_TALENT);
62    let deathblow_talent = params.talent_picked(TALENT::DEATHBLOW);
63    let soul_drinker = params.talent_picked(HERO::DARK_RANGER::SPELL::SOUL_DRINKER_TALENT);
64
65    let (aimed_chance, rf_chance) = gated(deathblow_talent, || {
66        (
67            base(EFFECT::DEATHBLOW_AIMED_PCT) / HUNDRED
68                + gated(soul_drinker, || base(EFFECT::SOUL_DRINKER_AIMED_PCT) / HUNDRED),
69            base(EFFECT::DEATHBLOW_RF_PCT) / HUNDRED
70                + gated(soul_drinker, || base(EFFECT::SOUL_DRINKER_RF_PCT) / HUNDRED),
71        )
72    });
73
74    let wf_count = wowlab_types::numeric::f64_to_i32_saturating_trunc(base(EFFECT::WITHERING_FIRE_COUNT))
75        + gated(params.talent_picked(HERO::DARK_RANGER::SPELL::BLIGHTED_QUIVER_TALENT), || wowlab_types::numeric::f64_to_i32_saturating_trunc(base(EFFECT::BLIGHTED_QUIVER_EXTRA)));
76
77    let dark_minion_duration_ms = data
78        .aura_duration_ms(SpellIdx::from_raw(HERO::DARK_RANGER::SPELL::DARK_MINION_SUMMON))
79        .unwrap_or_default();
80    let dark_minion_shoot_cast_ms = data
81        .cast_time_ms(SpellIdx::from_raw(REPORTED_SPELL::SHOOT))
82        .unwrap_or_default();
83}
84auras {}
85wiring {
86    { provenance: "MID2 Marksmanship two-piece Explosive Shot duration and periodic damage.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_MM_2PC), apply: {
87        let duration_ms = wowlab_types::numeric::f64_to_u32_saturating_trunc(base(EFFECT::MID2_MM_2PC_DURATION_MS));
88        let multiplier = 1.0 + base(EFFECT::MID2_MM_2PC_DAMAGE) / HUNDRED;
89        built.patch_aura(AURA_EXPLOSIVE_SHOT, |aura| {
90            aura.base_duration_ms = aura.base_duration_ms.saturating_add(duration_ms);
91            if let Some(periodic) = aura.periodic.as_mut() {
92                periodic.effect = match periodic.effect {
93                    PeriodicKind::DamageAp { coef, is_physical } => PeriodicKind::DamageAp {
94                        coef: coef * multiplier,
95                        is_physical,
96                    },
97                    other => other,
98                };
99            }
100        });
101    } },
102    { provenance: "MID2 Marksmanship four-piece Rapid Fire damage bonus.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_MM_4PC), apply: {
103        let multiplier = 1.0 + base(EFFECT::MID2_MM_4PC_DAMAGE) / HUNDRED;
104        built.patch_spell(SPELL_RAPID_FIRE, |spell| {
105            spell.channel.tick_damage = spell.channel.tick_damage.scaled(multiplier);
106        });
107    } },
108}
109finish |cfg| {
110    let _ = built;
111    cfg
112}
113}