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)]
135#[serde(deny_unknown_fields)]
136pub struct MaskedPassiveEffectDef {
137 pub spell_id: u32,
138 pub effect: u8,
139 pub reason: String,
140}
141
142#[derive(Debug, Deserialize)]
148#[serde(deny_unknown_fields)]
149pub struct PassiveEffectOverrideDef {
150 pub spell_id: u32,
151 pub effect: u8,
152 #[serde(default)]
153 pub from: Option<EffectRef>,
154 #[serde(default)]
155 pub value: Option<f64>,
156 #[serde(default)]
157 pub multiplier: Option<f64>,
158 pub reason: String,
159}
160
161#[derive(Debug, Deserialize)]
163#[serde(deny_unknown_fields)]
164pub struct EffectRef {
165 pub spell_id: u32,
166 pub effect: u8,
167}
168
169#[derive(Debug, Deserialize)]
175#[serde(deny_unknown_fields)]
176pub struct TalentGatedAuraEffectDef {
177 pub talent: String,
178 #[serde(default)]
179 pub target_aura: Option<String>,
180 #[serde(flatten)]
181 pub source: EffectRef,
182}
183
184#[derive(Debug, Deserialize)]
186#[serde(deny_unknown_fields)]
187pub struct ContentEffectCoverage {
188 pub spell_id: u32,
189 pub effect: u8,
190 pub status: ContentEffectStatus,
191 pub reason: String,
192}
193
194#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
196#[serde(rename_all = "snake_case")]
197#[non_exhaustive]
198pub enum ContentEffectStatus {
199 Modeled,
200 Ignored,
201}
202
203#[derive(Debug, Deserialize)]
204pub struct ImpactProcDef {
205 #[serde(default)]
206 pub spell: Option<String>,
207 #[serde(default)]
208 pub auto_attack_hand: Option<String>,
209 #[serde(default)]
210 pub proc_type_mask_spell_id: Option<u32>,
211 #[serde(default)]
212 pub proc_type_mask_effect: Option<u8>,
213 #[serde(default)]
214 pub talent: Option<String>,
215 #[serde(default)]
216 pub hook: Option<String>,
217 pub chance_pct: ScalarRef,
218 #[serde(default)]
219 pub rng_model: ImpactProcRngDef,
220 #[serde(default)]
221 pub icd_ms: Option<ScalarRef>,
222 #[serde(default)]
223 pub target_icd_ms: u32,
224 #[serde(default)]
225 pub phase: Vec<ImpactProcPhaseDef>,
226 #[serde(default)]
227 pub hit_mask: Vec<ImpactProcHitDef>,
228 #[serde(default)]
229 pub charges: u8,
230 #[serde(default)]
231 pub periodic_only: bool,
232 #[serde(default)]
233 pub skip_periodic: bool,
234 #[serde(default)]
235 pub physical_only: bool,
236 #[serde(default)]
237 pub effects: Vec<EventEffectDef>,
238}
239
240#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
241#[serde(tag = "kind", rename_all = "snake_case")]
242#[non_exhaustive]
243pub enum ImpactProcRngDef {
244 #[default]
245 Fixed,
246 WeaponSpeed {
247 baseline_ms: u32,
248 },
249 Shuffled {
250 success_entries: u32,
251 total_entries: u32,
252 },
253 Accumulated {
254 #[serde(default)]
255 cap: u32,
256 #[serde(default)]
257 initial_count: u32,
258 },
259}
260
261#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
262#[serde(rename_all = "snake_case")]
263#[non_exhaustive]
264pub enum ImpactProcPhaseDef {
265 Cast,
266 Hit,
267 Finish,
268}
269
270#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
271#[serde(rename_all = "snake_case")]
272#[non_exhaustive]
273pub enum ImpactProcHitDef {
274 Normal,
275 Critical,
276 Miss,
277 Dodge,
278 Parry,
279 Glance,
280 Block,
281 Absorb,
282 Landed,
283}
284
285#[derive(Debug, Deserialize)]
286#[serde(tag = "kind", rename_all = "snake_case")]
287#[non_exhaustive]
288pub enum EventEffectDef {
289 Energize {
290 amount: ScalarRef,
291 #[serde(default)]
292 secondary: bool,
293 },
294 ExtendAura {
295 aura: String,
296 amount_ms: ScalarRef,
297 },
298}
299
300#[derive(Debug, Deserialize)]
302pub struct SpecSection {
303 pub id: u32,
304 #[serde(default)]
305 pub has_pet: bool,
306 #[serde(default)]
307 pub custom_handler: Option<String>,
308 #[serde(default)]
309 pub precombat_auras: Option<Vec<String>>,
310 #[serde(default)]
311 pub stealth_aura: Option<String>,
312}
313
314#[derive(Debug, Deserialize)]
316pub struct ResourceSection {
317 pub name: String,
318 #[serde(default)]
319 pub max: Option<f64>,
320 #[serde(default)]
321 pub regen: Option<f64>,
322 #[serde(default)]
323 pub starts_at: Option<f64>,
324 pub type_id: u8,
325}
326
327#[derive(Debug, Deserialize)]
329pub struct SecondaryResourceSection {
330 pub name: String,
331 #[serde(default)]
332 pub max: Option<f64>,
333 pub type_id: u8,
334}
335
336#[derive(Debug, Deserialize)]
338pub struct MetricKeysSection {
339 pub module_name: String,
340 pub keys: Vec<String>,
341}
342
343#[derive(Debug, Deserialize)]
345pub struct RotationSchema {
346 #[serde(default)]
347 pub fields: Vec<String>,
348}
349
350#[derive(Debug, Deserialize)]
352pub struct ManifestHeroTalentTree {
353 #[serde(default)]
354 pub spells: BTreeMap<String, u32>,
355 #[serde(default)]
356 pub auras: BTreeMap<String, u32>,
357}