wowlab_parsers/parsers/spell_desc/
effect_copies.rs1use super::{
4 precision::{power_of_ten, renders_percentage},
5 types::{
6 BinaryOperator, ExpressionBlockNode, ExpressionNode, SpellDescriptionNode, VariableNode,
7 },
8};
9
10#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct CrossSpellEffectCopy {
13 pub source_spell_id: u32,
15 pub source_effect: u8,
17 pub divisor: f64,
19}
20
21#[must_use]
32pub fn cross_spell_precision_copies(description: &str) -> Vec<CrossSpellEffectCopy> {
33 let parsed = super::parser::parse(description);
34 let mut copies = Vec::new();
35
36 collect_copies(&parsed.ast.nodes, &mut copies);
37
38 copies
39}
40
41fn collect_copies(nodes: &[SpellDescriptionNode], copies: &mut Vec<CrossSpellEffectCopy>) {
43 for (position, node) in nodes.iter().enumerate() {
44 match node {
45 SpellDescriptionNode::ExpressionBlock(block) => {
46 if !renders_percentage(nodes.get(position + 1)) {
47 continue;
48 }
49
50 if let Some(copy) = cross_spell_copy(block) {
51 copies.push(copy);
52 }
53 }
54
55 SpellDescriptionNode::Conditional(conditional) => {
56 for branch in &conditional.conditions {
57 collect_copies(&branch.content, copies);
58 }
59
60 if let Some(else_branch) = &conditional.else_branch {
61 collect_copies(else_branch, copies);
62 }
63 }
64
65 SpellDescriptionNode::Text(_)
66 | SpellDescriptionNode::Variable(_)
67 | SpellDescriptionNode::Pluralization(_)
68 | SpellDescriptionNode::Gender(_)
69 | SpellDescriptionNode::ColorCode(_) => {}
70 }
71 }
72}
73
74#[must_use]
82pub fn precision_rendered_effect_indexes(aura_description: &str) -> Vec<u8> {
83 let parsed = super::parser::parse(aura_description);
84 let mut indexes = Vec::new();
85
86 collect_rendered_effects(&parsed.ast.nodes, &mut indexes);
87
88 indexes
89}
90
91fn collect_rendered_effects(nodes: &[SpellDescriptionNode], indexes: &mut Vec<u8>) {
93 for (position, node) in nodes.iter().enumerate() {
94 match node {
95 SpellDescriptionNode::ExpressionBlock(block) => {
96 if !renders_percentage(nodes.get(position + 1)) {
97 continue;
98 }
99
100 if let Some(index) = own_effect_index(&block.expression) {
101 indexes.push(index);
102 }
103 }
104
105 SpellDescriptionNode::Conditional(conditional) => {
106 for branch in &conditional.conditions {
107 collect_rendered_effects(&branch.content, indexes);
108 }
109
110 if let Some(else_branch) = &conditional.else_branch {
111 collect_rendered_effects(else_branch, indexes);
112 }
113 }
114
115 SpellDescriptionNode::Text(_)
116 | SpellDescriptionNode::Variable(_)
117 | SpellDescriptionNode::Pluralization(_)
118 | SpellDescriptionNode::Gender(_)
119 | SpellDescriptionNode::ColorCode(_) => {}
120 }
121 }
122}
123
124fn cross_spell_copy(block: &ExpressionBlockNode) -> Option<CrossSpellEffectCopy> {
125 let ExpressionNode::Binary(binary) = &block.expression else {
126 return None;
127 };
128
129 if binary.operator != BinaryOperator::Div {
130 return None;
131 }
132
133 let ExpressionNode::Variable(variable) = binary.left.as_ref() else {
134 return None;
135 };
136 let VariableNode::CrossSpell(reference) = variable.as_ref() else {
137 return None;
138 };
139
140 if !reference.var_type.eq_ignore_ascii_case("s") {
141 return None;
142 }
143
144 Some(CrossSpellEffectCopy {
145 source_spell_id: reference.spell_id,
146 source_effect: reference.effect_index?,
147 divisor: power_of_ten(&binary.right)?,
148 })
149}
150
151fn own_effect_index(expression: &ExpressionNode) -> Option<u8> {
153 let ExpressionNode::Variable(variable) = expression else {
154 return None;
155 };
156 let VariableNode::Effect(effect) = variable.as_ref() else {
157 return None;
158 };
159
160 effect
161 .var_type
162 .eq_ignore_ascii_case("w")
163 .then_some(effect.effect_index)
164}
165
166#[cfg(test)]
167mod tests;