Skip to main content

wowlab_engine_domain/dbc/passives/
talent.rs

1use super::{AuraSubtypeKind, ClassId, SpellDataFlat, aura_kind};
2
3// Armor-specialization passives are omitted because the gear pipeline applies them.
4#[rustfmt::skip]
5const CLASS_PASSIVE_SPELLS: &[(ClassId, u32)] = &[
6    // #t:aligned
7    (ClassId::Warrior    , 3_127)  , // Parry
8    (ClassId::Warrior    , 123_829), // Block
9    (ClassId::Warrior    , 137_047), // Warrior
10    (ClassId::Warrior    , 319_158), // Victory Rush
11    (ClassId::Paladin    , 82_242) , // Parry
12    (ClassId::Paladin    , 115_675), // Boundless Conviction
13    (ClassId::Paladin    , 123_830), // Block
14    (ClassId::Paladin    , 137_026), // Paladin
15    (ClassId::Paladin    , 327_977), // Judgment
16    (ClassId::Hunter     , 93_321) , // Control Pet
17    (ClassId::Hunter     , 137_014), // Hunter
18    (ClassId::Hunter     , 157_443), // Critical Strikes
19    (ClassId::Rogue      , 1_860)  , // Safe Fall
20    (ClassId::Rogue      , 2_836)  , // Detect Traps
21    (ClassId::Rogue      , 82_245) , // Parry
22    (ClassId::Rogue      , 137_034), // Rogue
23    (ClassId::Rogue      , 157_442), // Critical Strikes
24    (ClassId::Priest     , 45_243) , // Focused Will
25    (ClassId::Priest     , 89_745) , // Mysticism
26    (ClassId::Priest     , 137_030), // Priest
27    (ClassId::Priest     , 186_440), // Shadow Mend
28    (ClassId::DeathKnight, 51_986) , // On a Pale Horse
29    (ClassId::DeathKnight, 82_246) , // Parry
30    (ClassId::DeathKnight, 137_005), // Death Knight
31    (ClassId::DeathKnight, 197_147), // Festering Wound
32    (ClassId::Shaman     , 20_608) , // Reincarnation
33    (ClassId::Shaman     , 123_831), // Block
34    (ClassId::Shaman     , 137_038), // Shaman
35    (ClassId::Shaman     , 318_044), // Lightning Bolt
36    (ClassId::Mage       , 89_744) , // Wizardry
37    (ClassId::Mage       , 121_039), // Mana Attunement
38    (ClassId::Mage       , 137_018), // Mage
39    (ClassId::Warlock    , 137_042), // Warlock
40    (ClassId::Warlock    , 246_985), // Soul Shards
41    (ClassId::Warlock    , 342_914), // Fear
42    (ClassId::Monk       , 8_647)  , // Mystic Touch
43    (ClassId::Monk       , 107_500), // <DND> Monk Energy Driver
44    (ClassId::Monk       , 116_812), // Parry
45    (ClassId::Monk       , 117_959), // Crackling Jade Lightning
46    (ClassId::Monk       , 130_610), // Monk
47    (ClassId::Monk       , 137_022), // Monk
48    (ClassId::Monk       , 157_361), // Roll Speed Controls
49    (ClassId::Druid      , 20_719) , // Feline Grace
50    (ClassId::Druid      , 125_972), // Feline Grace
51    (ClassId::Druid      , 137_009), // Druid
52    (ClassId::Druid      , 159_456), // Travel Form
53    (ClassId::Druid      , 276_012), // Aquatic Form
54    (ClassId::Druid      , 276_029), // Flight Form
55    (ClassId::Druid      , 343_238), // Entangling Roots
56    (ClassId::DemonHunter, 196_055), // Double Jump
57    (ClassId::DemonHunter, 197_130), // Glide
58    (ClassId::DemonHunter, 203_724), // Parry
59    (ClassId::DemonHunter, 212_611), // Demon Hunter
60    (ClassId::DemonHunter, 255_260), // Chaos Brand
61    (ClassId::DemonHunter, 320_364), // Immolation Aura
62    (ClassId::Evoker     , 353_167), // Evoker
63    (ClassId::Evoker     , 381_773), // Permeating Chill
64    (ClassId::Evoker     , 396_571), // Tempered Scales
65    (ClassId::Evoker     , 400_006), // Energizing Flame
66];
67
68/// Returns baseline class-passive spell IDs.
69pub fn class_passive_spell_ids(class: ClassId) -> impl Iterator<Item = u32> {
70    CLASS_PASSIVE_SPELLS
71        .iter()
72        .filter(move |(c, _)| *c == class)
73        .map(|(_, id)| *id)
74}
75
76/// One resource adjustment a selected talent's passive aura contributes.
77#[derive(Clone, Copy, Debug, PartialEq)]
78// #t(rust_non_exhaustive_on_public) fixed set of modelled aura kinds; consumers match exhaustively
79pub enum ResourcePassive {
80    MaxFlat(f64),
81    RegenPercent(f64),
82    RegenPerFiveSeconds(f64),
83    AutoAttackGainPercent(f64),
84    CostPercent(f64),
85}
86
87/// Returns resource adjustments from a selected passive talent.
88pub fn talent_resource_passives(
89    spell: &SpellDataFlat,
90    ranks: u8,
91    specialization_order: Option<i32>,
92) -> impl Iterator<Item = (i32, ResourcePassive)> + '_ {
93    let ranks = f64::from(ranks);
94    let rage_effect_count = spell
95        .effects
96        .iter()
97        .filter(|effect| {
98            aura_kind(effect) == Some(AuraSubtypeKind::ModRageGeneratedFromAutoAttacks)
99        })
100        .count();
101    // Multi-spec talents may have unrelated leading effects, so specialization order indexes
102    // only the resource variants rather than the spell's raw effect positions.
103    let specialization_order = specialization_order.and_then(|order| usize::try_from(order).ok());
104    let specialization_rage_effect = specialization_order.and_then(|order| {
105        spell
106            .effects
107            .iter()
108            .filter(|effect| {
109                aura_kind(effect) == Some(AuraSubtypeKind::ModRageGeneratedFromAutoAttacks)
110            })
111            .nth(order)
112    });
113    let specialization_rage_effect = specialization_rage_effect.map(|effect| effect.index);
114
115    spell
116        .effects
117        .iter()
118        .filter(move |_| spell.is_passive)
119        .filter_map(move |e| match aura_kind(e) {
120            Some(AuraSubtypeKind::ModIncreaseEnergy | AuraSubtypeKind::ModMaxResource) => Some((
121                e.misc_value_0,
122                ResourcePassive::MaxFlat(e.base_points * ranks),
123            )),
124            Some(AuraSubtypeKind::ModPowerRegenPercent) => Some((
125                e.misc_value_0,
126                ResourcePassive::RegenPercent(e.base_points * ranks),
127            )),
128            Some(AuraSubtypeKind::ModPowerRegen) => Some((
129                e.misc_value_0,
130                ResourcePassive::RegenPerFiveSeconds(e.base_points * ranks),
131            )),
132            Some(AuraSubtypeKind::ModRageGeneratedFromAutoAttacks)
133                if rage_effect_count <= 1 || specialization_rage_effect == Some(e.index) =>
134            {
135                Some((
136                    i32::from(u8::from(wowlab_types::combat::ResourceType::Rage)),
137                    ResourcePassive::AutoAttackGainPercent(e.base_points * ranks),
138                ))
139            }
140            Some(AuraSubtypeKind::ModManaCostPercent) => Some((
141                i32::from(u8::from(wowlab_types::combat::ResourceType::Mana)),
142                ResourcePassive::CostPercent(e.base_points * ranks),
143            )),
144            _ => None,
145        })
146}