1use std::collections::BTreeMap;
4
5use wowlab_manifest_schema::{Manifest, ManifestAuraDef, ManifestSpellDef};
6
7pub(super) fn spec_counts(manifest: &Manifest) -> BTreeMap<&'static str, usize> {
8 let mut counts = BTreeMap::new();
9
10 counts.insert("Effect bindings read by hooks", manifest.effects.len());
11 counts.insert(
12 "Spell hooks",
13 manifest
14 .spells
15 .values()
16 .filter(|spell| spell.hook.is_some())
17 .count(),
18 );
19 counts.insert(
20 "Aura lifecycle fields",
21 manifest.auras.values().map(aura_lifecycle_fields).sum(),
22 );
23 counts.insert(
24 "Authored spell damage programs",
25 manifest
26 .spells
27 .values()
28 .map(|spell| usize::from(spell.damage.is_some()) + spell.damage_effects.len())
29 .sum(),
30 );
31 counts.insert(
32 "Authored spell-to-aura programs",
33 manifest.spells.values().map(spell_aura_operations).sum(),
34 );
35 counts.insert(
36 "Periodic aura payloads",
37 manifest.auras.values().map(periodic_payloads).sum(),
38 );
39 counts.insert(
40 "Aura modifiers",
41 manifest.auras.values().map(aura_modifiers).sum(),
42 );
43 counts.insert(
44 "Content-effect declarations",
45 manifest.content_effects.len(),
46 );
47 counts.insert(
48 "Spell gates/overrides",
49 manifest.spells.values().map(spell_gates).sum(),
50 );
51 counts.insert(
52 "Core spell-property overrides",
53 manifest.spells.values().map(core_spell_overrides).sum(),
54 );
55 counts.insert(
56 "Aura hooks",
57 manifest
58 .auras
59 .values()
60 .map(|aura| {
61 usize::from(aura.expire_hook.is_some()) + usize::from(aura.tick_hook.is_some())
62 })
63 .sum(),
64 );
65 counts.insert(
66 "Mastery hooks",
67 1 + usize::from(manifest.mastery.crit_damage_hook.is_some()),
68 );
69 counts.insert(
70 "Resource literals/references",
71 manifest
72 .spells
73 .values()
74 .filter(|spell| spell.gain.is_some())
75 .count(),
76 );
77 counts.insert("Auto-attack definitions", manifest.auto_attacks.len());
78 counts.insert(
79 "Spec custom handlers",
80 usize::from(manifest.spec.custom_handler.is_some()),
81 );
82 counts.insert(
83 "Auto-attack hooks",
84 manifest
85 .auto_attacks
86 .values()
87 .filter(|attack| attack.hook.is_some())
88 .count(),
89 );
90 counts.insert(
91 "On-consume event operations",
92 manifest
93 .auras
94 .values()
95 .map(|aura| aura.on_consume.len())
96 .sum(),
97 );
98 counts.insert("Declarative impact procs", manifest.impact_procs.len());
99 counts.insert(
100 "Cooldown mutation declarations",
101 manifest.spells.values().map(cooldown_mutations).sum(),
102 );
103
104 counts
105}
106
107fn aura_lifecycle_fields(aura: &ManifestAuraDef) -> usize {
108 [
109 aura.duration_ms.is_some(),
110 aura.max_stacks.is_some(),
111 aura.no_pandemic,
112 aura.split_across_stacks,
113 aura.apply_at_max_stacks,
114 aura.async_stacks,
115 aura.periodic_damage_scales_with_stacks,
116 aura.reapply_on_expire,
117 aura.periodic_partial_tick,
118 aura.periodic_hasted.is_some(),
119 aura.precombat,
120 aura.snapshot,
121 ]
122 .into_iter()
123 .filter(|present| *present)
124 .count()
125}
126
127fn spell_aura_operations(spell: &ManifestSpellDef) -> usize {
128 usize::from(spell.applies_aura.is_some())
129 + spell.applies_auras.len()
130 + spell.extends_auras.len()
131}
132
133fn periodic_payloads(aura: &ManifestAuraDef) -> usize {
134 [
135 aura.periodic_remove_stack.is_some(),
136 aura.periodic_damage.is_some(),
137 aura.periodic_residual_damage.is_some(),
138 aura.periodic_resource.is_some(),
139 aura.periodic_resource_drain.is_some(),
140 aura.periodic_apply_aura.is_some(),
141 aura.periodic_hook.is_some(),
142 ]
143 .into_iter()
144 .filter(|present| *present)
145 .count()
146}
147
148fn aura_modifiers(aura: &ManifestAuraDef) -> usize {
149 [
150 aura.damage_mult.is_some(),
151 aura.damage_mult_stacking.is_some(),
152 aura.pet_damage_mult.is_some(),
153 aura.haste_per_stack.is_some(),
154 aura.attack_speed_per_stack.is_some(),
155 aura.auto_attack_damage_per_stack.is_some(),
156 aura.resource_gain_mult_per_stack.is_some(),
157 aura.regen_percent_per_stack.is_some(),
158 aura.max_resource_per_stack.is_some(),
159 aura.crit_per_stack.is_some(),
160 aura.mastery_per_stack.is_some(),
161 aura.primary_stat_percent_per_stack.is_some(),
162 aura.attack_power_percent_per_stack.is_some(),
163 aura.crit_rating_per_stack.is_some(),
164 aura.haste_rating_per_stack.is_some(),
165 aura.mastery_rating_per_stack.is_some(),
166 aura.versatility_rating_per_stack.is_some(),
167 aura.primary_stat_per_stack.is_some(),
168 aura.crit_chance_spells.is_some(),
169 aura.crit_damage_spells.is_some(),
170 aura.damage_percent_spells.is_some(),
171 aura.damage_percent_spells_linear.is_some(),
172 aura.cast_time_percent.is_some(),
173 aura.cast_time_percent_per_stack.is_some(),
174 aura.gcd_percent.is_some(),
175 aura.spell_cost_flat.is_some(),
176 aura.spell_cost_percent.is_some(),
177 aura.secondary_spell_cost_percent.is_some(),
178 aura.secondary_spell_cost_flat.is_some(),
179 ]
180 .into_iter()
181 .filter(|present| *present)
182 .count()
183 + aura.recharge_rate_percent.len()
184}
185
186fn spell_gates(spell: &ManifestSpellDef) -> usize {
187 [
188 spell.requires_aura_id.is_some(),
189 spell.requires_aura_min_stacks.is_some(),
190 spell.cost_free_when_aura.is_some(),
191 spell.consume_aura_on_cast.is_some(),
192 spell.cooldown_bypass_when_aura.is_some(),
193 spell.execute_below_pct.is_some(),
194 spell.instant_when_aura.is_some(),
195 spell.override_when_aura.is_some(),
196 spell.override_with_spell.is_some(),
197 spell.override_aura_min_stacks.is_some(),
198 spell.override_shares_base_cooldown,
199 spell.override_preserves_aura,
200 ]
201 .into_iter()
202 .filter(|present| *present)
203 .count()
204}
205
206fn core_spell_overrides(spell: &ManifestSpellDef) -> usize {
207 [
208 spell.cooldown.is_some(),
209 spell.cost.is_some(),
210 spell.travel_time_ms.is_some(),
211 spell.cast_time_ms.is_some(),
212 spell.gcd_ms.is_some(),
213 spell.charges.is_some(),
214 spell.charge_cd.is_some(),
215 spell.cooldown_hasted.is_some(),
216 spell.usable_while_casting,
217 !spell.breaks_stealth,
218 spell.guaranteed_crit,
219 !spell.infer_damage,
220 !spell.infer_applies_aura,
221 ]
222 .into_iter()
223 .filter(|present| *present)
224 .count()
225}
226
227fn cooldown_mutations(spell: &ManifestSpellDef) -> usize {
228 spell.reduces_cd.as_ref().map_or(0, Vec::len)
229 + usize::from(spell.reduces_cd_chance.is_some())
230 + usize::from(spell.resets_cd_while.is_some())
231}