wowlab_parsers/parsers/spell_desc/
crit_scaling.rs1use super::types::{SpellDescriptionNode, VariableNode};
4
5const CRIT_CHANCE_SHARE: &str = "% of your critical strike chance";
7
8#[must_use]
18pub fn crit_chance_scaled_effect_index(description: &str) -> Option<u8> {
19 let parsed = super::parser::parse(description);
20 let mut found = Vec::new();
21
22 collect_from_nodes(&parsed.ast.nodes, &mut found);
23 found.dedup();
25
26 match found.as_slice() {
27 [only] => Some(*only),
28 _ => None,
29 }
30}
31
32fn collect_from_nodes(nodes: &[SpellDescriptionNode], found: &mut Vec<u8>) {
34 for (position, node) in nodes.iter().enumerate() {
35 match node {
36 SpellDescriptionNode::Variable(variable) => {
37 let VariableNode::Effect(effect) = variable else {
38 continue;
39 };
40
41 if !effect.var_type.eq_ignore_ascii_case("s")
42 || !states_crit_chance_share(nodes.get(position + 1))
43 {
44 continue;
45 }
46
47 found.push(effect.effect_index);
48 }
49
50 SpellDescriptionNode::Conditional(conditional) => {
51 for branch in &conditional.conditions {
52 collect_from_nodes(&branch.content, found);
53 }
54
55 if let Some(else_branch) = &conditional.else_branch {
56 collect_from_nodes(else_branch, found);
57 }
58 }
59
60 SpellDescriptionNode::Text(_)
61 | SpellDescriptionNode::ExpressionBlock(_)
62 | SpellDescriptionNode::Pluralization(_)
63 | SpellDescriptionNode::Gender(_)
64 | SpellDescriptionNode::ColorCode(_) => {}
65 }
66 }
67}
68
69fn states_crit_chance_share(node: Option<&SpellDescriptionNode>) -> bool {
70 matches!(node, Some(SpellDescriptionNode::Text(text))
71 if text.value().to_ascii_lowercase().starts_with(CRIT_CHANCE_SHARE))
72}
73
74#[cfg(test)]
75mod tests;