1use std::collections::BTreeMap;
4
5use indexmap::IndexMap;
6use serde::Deserialize;
7
8use crate::{
9 AutoAttackDef, CURRENT_SCHEMA_VERSION, ManifestAuraDef, ManifestSpellDef, ScalarRef,
10 SpellGroupRule,
11};
12
13const fn default_schema_version() -> u32 {
14 CURRENT_SCHEMA_VERSION
15}
16
17#[derive(Debug, Deserialize)]
19pub struct SpellGroupDef {
20 pub rule: SpellGroupRule,
21 pub auras: Vec<String>,
22}
23
24#[derive(Debug, Deserialize)]
26pub struct MasterySection {
27 pub spell_id: u32,
28 pub hook: String,
29 #[serde(default)]
30 pub crit_damage_hook: Option<String>,
31}
32
33#[derive(Debug, Deserialize)]
35pub struct ItemsManifest {
36 #[serde(default = "default_schema_version")]
37 pub schema_version: u32,
38 #[serde(default)]
39 pub items: IndexMap<String, ItemManifestEntry>,
40}
41
42#[derive(Debug, Deserialize)]
44pub struct ItemManifestEntry {
45 pub id: u32,
46 #[serde(default)]
47 pub driver_spell_id: Option<u32>,
48 #[serde(default)]
49 pub auras: IndexMap<String, ManifestAuraDef>,
50 #[serde(default)]
51 pub spells: IndexMap<String, ManifestSpellDef>,
52 #[serde(default)]
53 pub effects: IndexMap<String, EffectRef>,
54 #[serde(default)]
55 pub player_cast_hook: Option<String>,
56 #[serde(default)]
57 pub player_impact_hook: Option<ItemImpactHookDef>,
58 #[serde(default)]
59 pub rppm: Option<f64>,
60 #[serde(default = "default_true")]
61 pub rppm_haste_scales: bool,
62}
63
64#[derive(Debug, Deserialize)]
66#[serde(deny_unknown_fields)]
67pub struct ItemImpactHookDef {
68 pub hook: String,
69 #[serde(default)]
70 pub periodic_only: bool,
71 #[serde(default)]
72 pub skip_periodic: bool,
73 #[serde(default)]
74 pub crit_only: bool,
75}
76
77pub(crate) const fn default_true() -> bool {
78 true
79}
80
81#[derive(Debug, Deserialize)]
84#[serde(deny_unknown_fields)]
85pub struct Manifest {
86 #[serde(default = "default_schema_version")]
87 pub schema_version: u32,
88 pub spec: SpecSection,
89 pub mastery: MasterySection,
90 pub resource: ResourceSection,
91 #[serde(default)]
92 pub secondary_resource: Option<SecondaryResourceSection>,
93 #[serde(default)]
94 pub auras: IndexMap<String, ManifestAuraDef>,
95 #[serde(default)]
96 pub spells: IndexMap<String, ManifestSpellDef>,
97 #[serde(default)]
98 pub auto_attacks: IndexMap<String, AutoAttackDef>,
99 #[serde(default)]
100 pub talents: BTreeMap<String, u32>,
101 #[serde(default)]
102 pub set_bonuses: BTreeMap<String, u32>,
103 #[serde(default)]
104 pub talent_companion_auras: BTreeMap<String, Vec<String>>,
105 #[serde(default)]
106 pub talent_gated_aura_effects: Vec<TalentGatedAuraEffectDef>,
107 #[serde(default)]
108 pub metric_keys: Option<MetricKeysSection>,
109 #[serde(default)]
110 pub rotation_schema: Option<RotationSchema>,
111 #[serde(default)]
112 pub hero_talents: IndexMap<String, ManifestHeroTalentTree>,
113 #[serde(default)]
114 pub spell_groups: Vec<SpellGroupDef>,
115 #[serde(default)]
116 pub effects: IndexMap<String, EffectRef>,
117 #[serde(default)]
118 pub content_effects: IndexMap<String, ContentEffectCoverage>,
119 #[serde(default)]
120 pub reported_spells: BTreeMap<String, u32>,
121 #[serde(default)]
122 pub impact_procs: Vec<ImpactProcDef>,
123 #[serde(default)]
124 pub masked_passive_effects: Vec<MaskedPassiveEffectDef>,
125 #[serde(default)]
126 pub passive_effect_overrides: Vec<PassiveEffectOverrideDef>,
127}
128#[derive(Debug, Deserialize)]
134#[serde(deny_unknown_fields)]
135pub struct MaskedPassiveEffectDef {
136 pub spell_id: u32,
137 pub effect: u8,
138 pub reason: String,
139}
140
141#[derive(Debug, Deserialize)]
147#[serde(deny_unknown_fields)]
148pub struct PassiveEffectOverrideDef {
149 pub spell_id: u32,
150 pub effect: u8,
151 #[serde(default)]
152 pub from: Option<EffectRef>,
153 #[serde(default)]
154 pub value: Option<f64>,
155 #[serde(default)]
156 pub multiplier: Option<f64>,
157 pub reason: String,
158}
159
160#[derive(Debug, Deserialize)]
162#[serde(deny_unknown_fields)]
163pub struct EffectRef {
164 pub spell_id: u32,
165 pub effect: u8,
166}
167
168#[derive(Debug, Deserialize)]
174#[serde(deny_unknown_fields)]
175pub struct TalentGatedAuraEffectDef {
176 pub talent: String,
177 #[serde(default)]
178 pub target_aura: Option<String>,
179 #[serde(flatten)]
180 pub source: EffectRef,
181}
182
183#[derive(Debug, Deserialize)]
185#[serde(deny_unknown_fields)]
186pub struct ContentEffectCoverage {
187 pub spell_id: u32,
188 pub effect: u8,
189 pub status: ContentEffectStatus,
190 pub reason: String,
191}
192
193#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
195#[serde(rename_all = "snake_case")]
196#[non_exhaustive]
197pub enum ContentEffectStatus {
198 Modeled,
199 Ignored,
200}
201
202#[derive(Debug, Deserialize)]
203pub struct ImpactProcDef {
204 #[serde(default)]
205 pub spell: Option<String>,
206 #[serde(default)]
207 pub auto_attack_hand: Option<String>,
208 #[serde(default)]
209 pub proc_type_mask_spell_id: Option<u32>,
210 #[serde(default)]
211 pub proc_type_mask_effect: Option<u8>,
212 #[serde(default)]
213 pub talent: Option<String>,
214 #[serde(default)]
215 pub hook: Option<String>,
216 pub chance_pct: ScalarRef,
217 #[serde(default)]
218 pub rng_model: ImpactProcRngDef,
219 #[serde(default)]
220 pub icd_ms: Option<ScalarRef>,
221 #[serde(default)]
222 pub target_icd_ms: u32,
223 #[serde(default)]
224 pub phase: Vec<ImpactProcPhaseDef>,
225 #[serde(default)]
226 pub hit_mask: Vec<ImpactProcHitDef>,
227 #[serde(default)]
228 pub charges: u8,
229 #[serde(default)]
230 pub periodic_only: bool,
231 #[serde(default)]
232 pub skip_periodic: bool,
233 #[serde(default)]
234 pub physical_only: bool,
235 #[serde(default)]
236 pub effects: Vec<EventEffectDef>,
237}
238
239#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
240#[serde(tag = "kind", rename_all = "snake_case")]
241#[non_exhaustive]
242pub enum ImpactProcRngDef {
243 #[default]
244 Fixed,
245 WeaponSpeed {
246 baseline_ms: u32,
247 },
248 Shuffled {
249 success_entries: u32,
250 total_entries: u32,
251 },
252 Accumulated {
253 #[serde(default)]
254 cap: u32,
255 #[serde(default)]
256 initial_count: u32,
257 },
258}
259
260#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
261#[serde(rename_all = "snake_case")]
262#[non_exhaustive]
263pub enum ImpactProcPhaseDef {
264 Cast,
265 Hit,
266 Finish,
267}
268
269#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
270#[serde(rename_all = "snake_case")]
271#[non_exhaustive]
272pub enum ImpactProcHitDef {
273 Normal,
274 Critical,
275 Miss,
276 Dodge,
277 Parry,
278 Glance,
279 Block,
280 Absorb,
281 Landed,
282}
283
284#[derive(Debug, Deserialize)]
285#[serde(tag = "kind", rename_all = "snake_case")]
286#[non_exhaustive]
287pub enum EventEffectDef {
288 Energize {
289 amount: ScalarRef,
290 #[serde(default)]
291 secondary: bool,
292 },
293 ExtendAura {
294 aura: String,
295 amount_ms: ScalarRef,
296 },
297}
298
299#[derive(Debug, Deserialize)]
301pub struct SpecSection {
302 pub id: u32,
303 #[serde(default)]
304 pub has_pet: bool,
305 #[serde(default)]
306 pub custom_handler: Option<String>,
307 #[serde(default)]
308 pub precombat_auras: Option<Vec<String>>,
309 #[serde(default)]
310 pub stealth_aura: Option<String>,
311}
312
313#[derive(Debug, Deserialize)]
315pub struct ResourceSection {
316 pub name: String,
317 #[serde(default)]
318 pub max: Option<f64>,
319 #[serde(default)]
320 pub regen: Option<f64>,
321 #[serde(default)]
322 pub starts_at: Option<f64>,
323 pub type_id: u8,
324}
325
326#[derive(Debug, Deserialize)]
328pub struct SecondaryResourceSection {
329 pub name: String,
330 #[serde(default)]
331 pub max: Option<f64>,
332 pub type_id: u8,
333}
334
335#[derive(Debug, Deserialize)]
337pub struct MetricKeysSection {
338 pub module_name: String,
339 pub keys: Vec<String>,
340}
341
342#[derive(Debug, Deserialize)]
344pub struct RotationSchema {
345 #[serde(default)]
346 pub fields: Vec<String>,
347}
348
349#[derive(Debug, Deserialize)]
351pub struct ManifestHeroTalentTree {
352 #[serde(default)]
353 pub spells: BTreeMap<String, u32>,
354 #[serde(default)]
355 pub auras: BTreeMap<String, u32>,
356}