1use anyhow::bail;
4use proc_macro2::TokenStream;
5use quote::quote;
6use wowlab_manifest_schema::ManifestDamageDef;
7
8use crate::{
9 gen_emit::{damage_flags, effect_lookup, spell_idx},
10 rust_source::{lit_f64, lit_int, rust_ident},
11};
12
13fn weapon_ap_type(name: &str) -> anyhow::Result<TokenStream> {
14 Ok(match name {
15 "mainhand" => quote!(wowlab_engine_combat::WeaponApType::MainHand),
16 "offhand" => quote!(wowlab_engine_combat::WeaponApType::OffHand),
17 "both" => quote!(wowlab_engine_combat::WeaponApType::Both),
18 "none" => quote!(wowlab_engine_combat::WeaponApType::None),
19 other => bail!("unknown ap_type: {other}"),
20 })
21}
22
23pub(crate) fn apply_damage(
24 expression: &TokenStream,
25 damage: &ManifestDamageDef,
26) -> anyhow::Result<TokenStream> {
27 Ok(match damage {
28 ManifestDamageDef::EffectRef {
29 spell_id,
30 effect,
31 kind,
32 school: Some(school),
33 ap_type,
34 } => {
35 let spell_id = spell_idx(*spell_id);
36 let effect = lit_int(effect);
37 let flags = damage_flags(*school);
38
39 match (kind.as_str(), ap_type.as_deref()) {
40 ("ap", None) => {
41 quote!(#expression.damage_ap(data.ap_coef(#spell_id, #effect), #flags))
42 }
43 ("ap", Some(ap_type)) => {
44 let ap_type = weapon_ap_type(ap_type)?;
45
46 quote! {
47 #expression.damage_ap_typed(
48 data.ap_coef(#spell_id, #effect),
49 #flags,
50 #ap_type,
51 )
52 }
53 }
54 ("sp", _) => {
55 quote!(#expression.damage_sp(data.sp_coef(#spell_id, #effect), #flags))
56 }
57 (other, _) => bail!("unknown damage kind: {other}"),
58 }
59 }
60 ManifestDamageDef::EffectRef {
61 spell_id,
62 effect,
63 kind,
64 school: None,
65 ap_type,
66 } => {
67 let lookup = effect_lookup("e!(data), *spell_id, *effect);
68
69 match (kind.as_str(), ap_type.as_deref()) {
70 ("ap", None) => quote!(#expression.damage_ap_from_data(#lookup)?),
71 ("ap", Some(ap_type)) => {
72 let ap_type = weapon_ap_type(ap_type)?;
73
74 quote!(#expression.damage_ap_from_data_typed(#lookup, #ap_type)?)
75 }
76 ("sp", _) => quote!(#expression.damage_sp_from_data(#lookup)?),
77 (other, _) => bail!("unknown damage kind: {other}"),
78 }
79 }
80 ManifestDamageDef::Ap { ap_coef, school } => {
81 let coefficient = lit_f64(*ap_coef);
82 let flags = damage_flags(*school);
83
84 quote!(#expression.damage_ap(#coefficient, #flags))
85 }
86 ManifestDamageDef::Sp { sp_coef, school } => {
87 let coefficient = lit_f64(*sp_coef);
88 let flags = damage_flags(*school);
89
90 quote!(#expression.damage_sp(#coefficient, #flags))
91 }
92 ManifestDamageDef::Flat { flat } => {
93 let flat = lit_f64(*flat);
94
95 quote!(#expression.damage_flat(#flat))
96 }
97 })
98}
99
100pub(crate) fn apply_channel_tick_damage(
101 expression: &TokenStream,
102 damage: &ManifestDamageDef,
103) -> anyhow::Result<TokenStream> {
104 Ok(match damage {
105 ManifestDamageDef::EffectRef {
106 spell_id,
107 effect,
108 kind,
109 school: Some(school),
110 ..
111 } => {
112 let spell_id = spell_idx(*spell_id);
113 let effect = lit_int(effect);
114 let flags = damage_flags(*school);
115
116 match kind.as_str() {
117 "ap" => quote! {
118 #expression.channel_tick_damage_ap(
119 data.ap_coef(#spell_id, #effect),
120 #flags,
121 )
122 },
123 "sp" => quote! {
124 #expression.channel_tick_damage_sp(
125 data.sp_coef(#spell_id, #effect),
126 #flags,
127 )
128 },
129 other => bail!("unknown channel tick damage kind: {other}"),
130 }
131 }
132 ManifestDamageDef::EffectRef {
133 spell_id,
134 effect,
135 kind,
136 school: None,
137 ..
138 } => {
139 let lookup = effect_lookup("e!(data), *spell_id, *effect);
140
141 match kind.as_str() {
142 "ap" => quote!(#expression.channel_tick_damage_ap_from_data(#lookup)?),
143 "sp" => quote!(#expression.channel_tick_damage_sp_from_data(#lookup)?),
144 other => bail!("unknown channel tick damage kind: {other}"),
145 }
146 }
147 ManifestDamageDef::Ap { ap_coef, school } => {
148 let coefficient = lit_f64(*ap_coef);
149 let flags = damage_flags(*school);
150
151 quote!(#expression.channel_tick_damage_ap(#coefficient, #flags))
152 }
153 ManifestDamageDef::Sp { sp_coef, school } => {
154 let coefficient = lit_f64(*sp_coef);
155 let flags = damage_flags(*school);
156
157 quote!(#expression.channel_tick_damage_sp(#coefficient, #flags))
158 }
159 ManifestDamageDef::Flat { .. } => {
160 bail!("channel tick_damage does not support Flat form");
161 }
162 })
163}
164
165pub(crate) fn apply_channel_tick_damage_alt(
166 expression: &TokenStream,
167 damage: &ManifestDamageDef,
168 aura: &str,
169) -> anyhow::Result<TokenStream> {
170 let aura = rust_ident(aura)?;
171
172 Ok(match damage {
173 ManifestDamageDef::EffectRef {
174 spell_id,
175 effect,
176 kind,
177 school: Some(school),
178 ..
179 } => {
180 let spell_idx = spell_idx(*spell_id);
181 let raw_spell_id = lit_int(spell_id);
182 let effect = lit_int(effect);
183 let flags = damage_flags(*school);
184
185 match kind.as_str() {
186 "ap" => quote! {
187 #expression.channel_tick_damage_alt_ap(
188 data.ap_coef(#spell_idx, #effect),
189 #flags,
190 #raw_spell_id,
191 AURA::#aura,
192 )
193 },
194 "sp" => quote! {
195 #expression.channel_tick_damage_alt_sp(
196 data.sp_coef(#spell_idx, #effect),
197 #flags,
198 #raw_spell_id,
199 AURA::#aura,
200 )
201 },
202 other => bail!("unknown alternative channel tick damage kind: {other}"),
203 }
204 }
205 ManifestDamageDef::EffectRef {
206 spell_id,
207 effect,
208 kind,
209 school: None,
210 ..
211 } => {
212 let lookup = effect_lookup("e!(data), *spell_id, *effect);
213
214 match kind.as_str() {
215 "ap" => {
216 quote!(#expression.channel_tick_damage_alt_ap_from_data(#lookup, AURA::#aura)?)
217 }
218 "sp" => {
219 quote!(#expression.channel_tick_damage_alt_sp_from_data(#lookup, AURA::#aura)?)
220 }
221 other => bail!("unknown alternative channel tick damage kind: {other}"),
222 }
223 }
224 ManifestDamageDef::Ap { ap_coef, school } => {
225 let coefficient = lit_f64(*ap_coef);
226 let flags = damage_flags(*school);
227
228 quote! {
229 #expression.channel_tick_damage_alt_ap(
230 #coefficient,
231 #flags,
232 0,
233 AURA::#aura,
234 )
235 }
236 }
237 ManifestDamageDef::Sp { sp_coef, school } => {
238 let coefficient = lit_f64(*sp_coef);
239 let flags = damage_flags(*school);
240
241 quote! {
242 #expression.channel_tick_damage_alt_sp(
243 #coefficient,
244 #flags,
245 0,
246 AURA::#aura,
247 )
248 }
249 }
250 ManifestDamageDef::Flat { .. } => {
251 bail!("alternative channel tick damage does not support Flat form");
252 }
253 })
254}
255
256#[cfg(test)]
257mod tests {
258 use googletest::prelude::*;
259
260 use super::*;
261
262 #[gtest]
263 fn stage_qualified_manifest_damage_emits_the_existing_builder_call() -> Result<()> {
264 let damage: ManifestDamageDef = toml::from_str(
265 r#"
266 ap_coef = 1.25
267 school = "physical"
268 "#,
269 )
270 .or_fail()?;
271
272 verify_that!(
273 apply_damage("e!(spell), &damage).or_fail()?.to_string(),
274 eq("spell . damage_ap (1.25 , wowlab_engine_combat :: DamageFlags :: PHYSICAL)")
275 )
276 }
277}