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