codegen/
gen_spell_properties.rs1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3use wowlab_manifest_schema::ManifestSpellDef;
4
5use crate::{
6 gen_emit::{
7 scalar_ref_call, spell_idx, validated_charges_call, validated_milliseconds_call,
8 validated_nonnegative_call,
9 },
10 rust_source::{lit_int, rust_ident, rust_path},
11};
12
13pub(crate) fn apply_core_spell_properties(
14 mut expression: TokenStream,
15 id: u32,
16 def: &ManifestSpellDef,
17 hook_module: &str,
18) -> anyhow::Result<TokenStream> {
19 if let Some(cooldown) = &def.cooldown {
20 let cooldown = validated_nonnegative_call(cooldown, id, "spell.cooldown")?;
21
22 expression = quote!(#expression.cooldown(#cooldown));
23 }
24
25 if let Some(charges) = &def.charges {
26 let charge_cooldown = def
27 .charge_cd
28 .as_ref()
29 .or(def.cooldown.as_ref())
30 .map(|value| validated_nonnegative_call(value, id, "spell.charge_cd"))
31 .transpose()?
32 .unwrap_or_else(|| {
33 let spell_id = spell_idx(id);
34
35 quote!(data.require_charge_cd_s(#spell_id)?)
36 });
37 let charges = validated_charges_call(charges, id, "spell.charges")?;
38
39 expression = quote!(#expression.charges(#charges, #charge_cooldown));
40 }
41
42 if let Some(hasted) = def.cooldown_hasted {
43 expression = if hasted {
44 quote!(#expression.hasted_cooldown())
45 } else {
46 quote!(#expression.unhasted_cooldown())
47 };
48 }
49
50 if def.cast_time_ms.is_some() && def.empower.is_some() {
51 anyhow::bail!("spell {id} cannot declare both cast_time_ms and empower");
52 }
53
54 if let Some(empower) = &def.empower {
55 if let Some(aura) = &empower.max_when_aura {
56 let aura = rust_ident(aura)?;
57
58 expression = quote!(#expression.max_empower_when_aura(AURA::#aura));
59 }
60
61 if let Some(driver) = &empower.driver {
62 let hook_module = rust_path(hook_module)?;
63 let driver = format_ident!("{driver}_empower_release");
64
65 expression =
66 quote!(#expression.on_empower_release(crate::hooks::#hook_module::#driver));
67 }
68 } else if let Some(cast_time) = &def.cast_time_ms {
69 let cast_time = validated_milliseconds_call(cast_time, id, "spell.cast_time_ms")?;
70
71 expression = quote!(#expression.cast_time(#cast_time));
72 }
73
74 if let Some(travel_time) = &def.travel_time_ms {
75 let travel_time = validated_milliseconds_call(travel_time, id, "spell.travel_time_ms")?;
76
77 expression = quote!(#expression.travel_time(#travel_time));
78 }
79
80 if let Some(cost) = &def.cost {
81 let cost = validated_nonnegative_call(cost, id, "spell.cost")?;
82
83 expression = quote!(#expression.cost(#cost));
84 }
85
86 if let Some(gcd) = &def.gcd_ms {
87 let gcd = validated_milliseconds_call(gcd, id, "spell.gcd_ms")?;
88
89 expression = quote!(#expression.gcd(#gcd));
90 }
91
92 if let Some(gain) = &def.gain {
93 let gain = scalar_ref_call(gain)?;
94
95 expression = quote!(#expression.gain(#gain));
96 }
97
98 Ok(expression)
99}
100
101pub(crate) fn apply_override(
102 mut expression: TokenStream,
103 id: u32,
104 def: &ManifestSpellDef,
105) -> anyhow::Result<TokenStream> {
106 match (&def.override_when_aura, &def.override_with_spell) {
107 (Some(aura), Some(spell)) => {
108 let aura = rust_ident(aura)?;
109 let spell = rust_ident(spell)?;
110
111 expression = quote!(#expression.overridden_by(SPELL::#spell, AURA::#aura));
112 }
113 (None, None) => {}
114 _ => anyhow::bail!(
115 "spell {id}: override_when_aura and override_with_spell must be specified together"
116 ),
117 }
118
119 if let Some(min_stacks) = def.override_aura_min_stacks {
120 let min_stacks = lit_int(min_stacks);
121
122 expression = quote!(#expression.override_at_min_stacks(#min_stacks));
123 }
124
125 if def.override_shares_base_cooldown {
126 expression = quote!(#expression.override_shares_base_cooldown());
127 }
128
129 if def.override_preserves_aura {
130 expression = quote!(#expression.override_preserves_aura());
131 }
132
133 Ok(expression)
134}