Skip to main content

wowlab_parsers/parsers/dbc/rows/
spell.rs

1// #t(file: rust_duplicate_strings) DBC field attributes repeat an upstream-schema ownership reason that Rust requires as a literal.
2
3use serde::Deserialize;
4use wowlab_engine_macros::{HasFk, HasId};
5
6/// DBC row from SpellName.db2: spell ID and localized name.
7#[derive(Clone, Debug, Deserialize, HasId)]
8#[allow(
9    non_snake_case,
10    reason = "fields mirror upstream DBC CSV headers exactly"
11)]
12pub struct SpellNameRow {
13    pub ID: i32,
14    pub Name_lang: Option<String>,
15}
16
17/// DBC row from Spell.db2: subtext and description text.
18#[derive(Clone, Debug, Deserialize, HasId)]
19#[allow(
20    non_snake_case,
21    reason = "fields mirror upstream DBC CSV headers exactly"
22)]
23pub struct SpellRow {
24    pub ID: i32,
25    pub NameSubtext_lang: Option<String>,
26    pub Description_lang: Option<String>,
27    pub AuraDescription_lang: Option<String>,
28}
29
30/// DBC row from SpellMisc.db2: attribute flags, school mask, speed, and timing indices.
31#[derive(Clone, Debug, Deserialize, HasFk)]
32#[allow(
33    non_snake_case,
34    reason = "fields mirror upstream DBC CSV headers exactly"
35)]
36#[fk_field = "SpellID"]
37pub struct SpellMiscRow {
38    pub ID: i32,
39    pub Attributes_0: i32,
40    pub Attributes_1: i32,
41    pub Attributes_2: i32,
42    pub Attributes_3: i32,
43    pub Attributes_4: i32,
44    pub Attributes_5: i32,
45    pub Attributes_6: i32,
46    pub Attributes_7: i32,
47    pub Attributes_8: i32,
48    pub Attributes_9: i32,
49    pub Attributes_10: i32,
50    pub Attributes_11: i32,
51    pub Attributes_12: i32,
52    pub Attributes_13: i32,
53    pub Attributes_14: i32,
54    pub Attributes_15: i32,
55    pub Attributes_16: i32,
56    pub DifficultyID: i32,
57    pub CastingTimeIndex: i32,
58    pub DurationIndex: i32,
59    pub PvPDurationIndex: i32,
60    pub RangeIndex: i32,
61    pub SchoolMask: i32,
62    pub Speed: f32,
63    pub LaunchDelay: f32,
64    pub MinDuration: f32,
65    pub SpellIconFileDataID: i32,
66    pub ActiveIconFileDataID: i32,
67    pub ContentTuningID: i32,
68    pub ShowFutureSpellPlayerConditionID: i32,
69    pub SpellVisualScript: i32,
70    pub ActiveSpellVisualScript: i32,
71    pub SpellID: i32,
72}
73
74impl SpellMiscRow {
75    /// All available attribute flags (`Attributes_0`..`Attributes_16`) as a Vec.
76    pub fn attributes_vec(&self) -> Vec<i32> {
77        vec![
78            self.Attributes_0,
79            self.Attributes_1,
80            self.Attributes_2,
81            self.Attributes_3,
82            self.Attributes_4,
83            self.Attributes_5,
84            self.Attributes_6,
85            self.Attributes_7,
86            self.Attributes_8,
87            self.Attributes_9,
88            self.Attributes_10,
89            self.Attributes_11,
90            self.Attributes_12,
91            self.Attributes_13,
92            self.Attributes_14,
93            self.Attributes_15,
94            self.Attributes_16,
95        ]
96    }
97}
98
99/// DBC row from SpellEffect.db2: effect type, coefficients, targets, and scaling data.
100#[derive(Clone, Debug, Deserialize, HasFk)]
101#[allow(
102    non_snake_case,
103    reason = "fields mirror upstream DBC CSV headers exactly"
104)]
105#[fk_field = "SpellID"]
106pub struct SpellEffectRow {
107    pub ID: i32,
108    pub EffectAura: i32,
109    pub DifficultyID: i32,
110    pub EffectIndex: i32,
111    pub Effect: i32,
112    pub EffectAmplitude: f32,
113    pub EffectAttributes: i32,
114    pub EffectAuraPeriod: i32,
115    pub EffectBonusCoefficient: f64,
116    pub EffectChainAmplitude: f32,
117    pub EffectChainTargets: i32,
118    pub EffectItemType: i32,
119    pub EffectMechanic: i32,
120    pub EffectPointsPerResource: f32,
121    pub EffectPos_facing: f32,
122    pub EffectRealPointsPerLevel: f32,
123    pub EffectTriggerSpell: i32,
124    pub BonusCoefficientFromAP: f64,
125    pub PvpMultiplier: f32,
126    pub Coefficient: f32,
127    pub Variance: f32,
128    pub ResourceCoefficient: f32,
129    pub GroupSizeBasePointsCoefficient: f32,
130    pub EffectBasePointsF: f64,
131    pub ScalingClass: i32,
132    #[serde(rename = "Node__Field_12_0_0_63534_001")]
133    pub Node_Field: i32,
134    pub EffectMiscValue_0: i32,
135    pub EffectMiscValue_1: i32,
136    pub EffectRadiusIndex_0: i32,
137    pub EffectRadiusIndex_1: i32,
138    pub EffectSpellClassMask_0: i32,
139    pub EffectSpellClassMask_1: i32,
140    pub EffectSpellClassMask_2: i32,
141    pub EffectSpellClassMask_3: i32,
142    pub ImplicitTarget_0: i32,
143    pub ImplicitTarget_1: i32,
144    pub SpellID: i32,
145}
146
147/// DBC row from SpellShapeshiftForm.db2: form identity and behavior flags.
148#[derive(Clone, Debug, Deserialize, HasId)]
149#[allow(
150    non_snake_case,
151    reason = "fields mirror upstream DBC CSV headers exactly"
152)]
153pub struct SpellShapeshiftFormRow {
154    pub ID: i32,
155    pub Flags: i32,
156    pub CombatRoundTime: i32,
157}
158
159/// DBC row from SpellPower.db2: resource cost and power type for a spell.
160#[derive(Clone, Debug, Deserialize, HasFk)]
161#[allow(
162    non_snake_case,
163    reason = "fields mirror upstream DBC CSV headers exactly"
164)]
165#[fk_field = "SpellID"]
166pub struct SpellPowerRow {
167    pub ID: i32,
168    pub OrderIndex: i32,
169    pub ManaCost: i32,
170    pub ManaCostPerLevel: i32,
171    pub ManaPerSecond: i32,
172    pub PowerDisplayID: i32,
173    pub AltPowerBarID: i32,
174    pub PowerCostPct: f64,
175    pub PowerCostMaxPct: f32,
176    pub OptionalCostPct: f32,
177    pub PowerPctPerSecond: f32,
178    pub PowerType: i32,
179    pub RequiredAuraSpellID: i32,
180    pub OptionalCost: i32,
181    pub SpellID: i32,
182}
183
184/// DBC row from SpellCooldowns.db2: cooldown and global cooldown timers.
185#[derive(Clone, Debug, Deserialize, HasFk)]
186#[allow(
187    non_snake_case,
188    reason = "fields mirror upstream DBC CSV headers exactly"
189)]
190#[fk_field = "SpellID"]
191pub struct SpellCooldownsRow {
192    pub ID: i32,
193    pub DifficultyID: i32,
194    pub CategoryRecoveryTime: i32,
195    pub RecoveryTime: i32,
196    pub StartRecoveryTime: i32,
197    pub AuraSpellID: i32,
198    pub SpellID: i32,
199}
200
201/// DBC row from SpellCategories.db2: category, dispel type, and defense type.
202#[derive(Clone, Debug, Deserialize, HasFk)]
203#[allow(
204    non_snake_case,
205    reason = "fields mirror upstream DBC CSV headers exactly"
206)]
207#[fk_field = "SpellID"]
208pub struct SpellCategoriesRow {
209    pub ID: i32,
210    pub DifficultyID: i32,
211    pub Category: i32,
212    pub DefenseType: i32,
213    pub DiminishType: i32,
214    pub DispelType: i32,
215    pub Mechanic: i32,
216    pub PreventionType: i32,
217    pub StartRecoveryCategory: i32,
218    pub ChargeCategory: i32,
219    pub SpellID: i32,
220}
221
222/// DBC row from SpellCategory.db2: category charge recovery settings.
223#[derive(Clone, Debug, Deserialize, HasId)]
224#[allow(
225    non_snake_case,
226    reason = "fields mirror upstream DBC CSV headers exactly"
227)]
228pub struct SpellCategoryRow {
229    pub ID: i32,
230    pub Name_lang: Option<String>,
231    pub Flags: i32,
232    pub UsesPerWeek: i32,
233    pub MaxCharges: i32,
234    pub ChargeRecoveryTime: i32,
235    pub TypeMask: i32,
236}
237
238/// DBC row from SpellCastTimes.db2: base and minimum cast time.
239#[derive(Clone, Debug, Deserialize, HasId)]
240#[allow(
241    non_snake_case,
242    reason = "fields mirror upstream DBC CSV headers exactly"
243)]
244pub struct SpellCastTimesRow {
245    pub ID: i32,
246    pub Base: i32,
247    pub Minimum: i32,
248}
249
250/// DBC row from SpellDuration.db2: base and maximum duration in milliseconds.
251#[derive(Clone, Debug, Deserialize, HasId)]
252#[allow(
253    non_snake_case,
254    reason = "fields mirror upstream DBC CSV headers exactly"
255)]
256pub struct SpellDurationRow {
257    pub ID: i32,
258    pub Duration: i32,
259    pub MaxDuration: i32,
260    pub DurationPerResource: i32,
261}
262
263/// DBC row from SpellRange.db2: minimum and maximum range distances.
264#[derive(Clone, Debug, Deserialize, HasId)]
265#[allow(
266    non_snake_case,
267    reason = "fields mirror upstream DBC CSV headers exactly"
268)]
269pub struct SpellRangeRow {
270    pub ID: i32,
271    pub DisplayName_lang: Option<String>,
272    pub DisplayNameShort_lang: Option<String>,
273    pub Flags: i32,
274    pub RangeMin_0: f32,
275    pub RangeMin_1: f32,
276    pub RangeMax_0: f32,
277    pub RangeMax_1: f32,
278}
279
280/// DBC row from SpellRadius.db2: minimum, maximum, and base radius values.
281#[derive(Clone, Debug, Deserialize, HasId)]
282#[allow(
283    non_snake_case,
284    reason = "fields mirror upstream DBC CSV headers exactly"
285)]
286pub struct SpellRadiusRow {
287    pub ID: i32,
288    pub Radius: f32,
289    pub RadiusPerLevel: f32,
290    pub RadiusMin: f32,
291    pub RadiusMax: f32,
292}
293
294/// DBC row from SpellClassOptions.db2: spell class set and family flags.
295#[derive(Clone, Debug, Deserialize, HasFk)]
296#[allow(
297    non_snake_case,
298    reason = "fields mirror upstream DBC CSV headers exactly"
299)]
300#[fk_field = "SpellID"]
301pub struct SpellClassOptionsRow {
302    pub ID: i32,
303    pub SpellID: i32,
304    pub ModalNextSpell: i32,
305    pub SpellClassSet: i32,
306    pub SpellClassMask_0: i32,
307    pub SpellClassMask_1: i32,
308    pub SpellClassMask_2: i32,
309    pub SpellClassMask_3: i32,
310}
311
312/// DBC row from SpellAuraRestrictions.db2: required and excluded aura/state conditions.
313#[derive(Clone, Debug, Deserialize, HasFk)]
314#[allow(
315    non_snake_case,
316    reason = "fields mirror upstream DBC CSV headers exactly"
317)]
318#[fk_field = "SpellID"]
319pub struct SpellAuraRestrictionsRow {
320    pub ID: i32,
321    pub DifficultyID: i32,
322    pub CasterAuraState: i32,
323    pub TargetAuraState: i32,
324    pub ExcludeCasterAuraState: i32,
325    pub ExcludeTargetAuraState: i32,
326    pub CasterAuraSpell: i32,
327    pub TargetAuraSpell: i32,
328    pub ExcludeCasterAuraSpell: i32,
329    pub ExcludeTargetAuraSpell: i32,
330    pub CasterAuraType: i32,
331    pub TargetAuraType: i32,
332    pub ExcludeCasterAuraType: i32,
333    pub ExcludeTargetAuraType: i32,
334    pub SpellID: i32,
335}
336
337/// DBC row from SpellInterrupts.db2: interrupt flags and channel interrupt masks.
338#[derive(Clone, Debug, Deserialize, HasFk)]
339#[allow(
340    non_snake_case,
341    reason = "fields mirror upstream DBC CSV headers exactly"
342)]
343#[fk_field = "SpellID"]
344pub struct SpellInterruptsRow {
345    pub ID: i32,
346    pub DifficultyID: i32,
347    pub InterruptFlags: i32,
348    pub AuraInterruptFlags_0: i32,
349    pub AuraInterruptFlags_1: i32,
350    pub ChannelInterruptFlags_0: i32,
351    pub ChannelInterruptFlags_1: i32,
352    pub SpellID: i32,
353}
354
355/// DBC row from SpellEmpower.db2: empower stage configuration for a spell.
356#[derive(Clone, Debug, Deserialize, HasFk)]
357#[allow(
358    non_snake_case,
359    reason = "fields mirror upstream DBC CSV headers exactly"
360)]
361#[fk_field = "SpellID"]
362pub struct SpellEmpowerRow {
363    pub ID: i32,
364    pub SpellID: i32,
365    #[serde(rename = "Field_10_0_0_44649_002")]
366    pub Field: i32,
367}
368
369/// DBC row from SpellEmpowerStage.db2: individual empower stage duration and spell.
370#[derive(Clone, Debug, Deserialize, HasFk)]
371#[allow(
372    non_snake_case,
373    reason = "fields mirror upstream DBC CSV headers exactly"
374)]
375#[fk_field = "SpellEmpowerID"]
376pub struct SpellEmpowerStageRow {
377    pub ID: i32,
378    pub Stage: i32,
379    pub DurationMs: i32,
380    pub SpellEmpowerID: i32,
381}
382
383/// DBC row from SpellTargetRestrictions.db2: cone angle, max targets, and target flags.
384#[derive(Clone, Debug, Deserialize, HasFk)]
385#[allow(
386    non_snake_case,
387    reason = "fields mirror upstream DBC CSV headers exactly"
388)]
389#[fk_field = "SpellID"]
390pub struct SpellTargetRestrictionsRow {
391    pub ID: i32,
392    pub DifficultyID: i32,
393    pub ConeDegrees: f32,
394    pub MaxTargets: i32,
395    pub MaxTargetLevel: i32,
396    pub TargetCreatureType: i32,
397    pub Targets: i32,
398    pub Width: f32,
399    pub SpellID: i32,
400}
401
402/// DBC row from SpellEquippedItems.db2: item class, subclass, and inventory restrictions.
403#[derive(Clone, Debug, Deserialize, HasFk)]
404#[allow(
405    non_snake_case,
406    reason = "fields mirror upstream DBC CSV headers exactly"
407)]
408#[fk_field = "SpellID"]
409pub struct SpellEquippedItemsRow {
410    pub ID: i32,
411    pub SpellID: i32,
412    pub EquippedItemClass: i32,
413    pub EquippedItemInvTypes: i32,
414    pub EquippedItemSubclass: i32,
415}
416
417/// DBC row from SpellLevels.db2: base level, max level, and spell level.
418#[derive(Clone, Debug, Deserialize, HasFk)]
419#[allow(
420    non_snake_case,
421    reason = "fields mirror upstream DBC CSV headers exactly"
422)]
423#[fk_field = "SpellID"]
424pub struct SpellLevelsRow {
425    pub ID: i32,
426    pub DifficultyID: i32,
427    pub MaxLevel: i32,
428    pub MaxPassiveAuraLevel: i32,
429    pub BaseLevel: i32,
430    pub SpellLevel: i32,
431    pub SpellID: i32,
432}
433
434/// DBC row from SpellLearnSpell.db2: spells learned when a parent spell is known.
435#[derive(Clone, Debug, Deserialize, HasFk)]
436#[allow(
437    non_snake_case,
438    reason = "fields mirror upstream DBC CSV headers exactly"
439)]
440#[fk_field = "SpellID"]
441pub struct SpellLearnSpellRow {
442    pub ID: i32,
443    pub SpellID: i32,
444    pub LearnSpellID: i32,
445    pub OverridesSpellID: i32,
446}
447
448/// DBC row from SpellReplacement.db2: replacement spell overrides.
449#[derive(Clone, Debug, Deserialize, HasFk)]
450#[allow(
451    non_snake_case,
452    reason = "fields mirror upstream DBC CSV headers exactly"
453)]
454#[fk_field = "SpellID"]
455pub struct SpellReplacementRow {
456    pub ID: i32,
457    pub ReplacementSpellID: i32,
458    pub SpellID: i32,
459}
460
461/// DBC row from SpellShapeshift.db2: shapeshift form requirements and exclusions.
462#[derive(Clone, Debug, Deserialize, HasFk)]
463#[allow(
464    non_snake_case,
465    reason = "fields mirror upstream DBC CSV headers exactly"
466)]
467#[fk_field = "SpellID"]
468pub struct SpellShapeshiftRow {
469    pub ID: i32,
470    pub SpellID: i32,
471    pub StanceBarOrder: i32,
472    pub ShapeshiftExclude_0: i32,
473    pub ShapeshiftExclude_1: i32,
474    pub ShapeshiftMask_0: i32,
475    pub ShapeshiftMask_1: i32,
476}
477
478/// DBC row from SpellTotems.db2: totem and totem category requirements.
479#[derive(Clone, Debug, Deserialize, HasFk)]
480#[allow(
481    non_snake_case,
482    reason = "fields mirror upstream DBC CSV headers exactly"
483)]
484#[fk_field = "SpellID"]
485pub struct SpellTotemsRow {
486    pub ID: i32,
487    pub SpellID: i32,
488    pub RequiredTotemCategoryID_0: i32,
489    pub RequiredTotemCategoryID_1: i32,
490    pub Totem_0: i32,
491    pub Totem_1: i32,
492}
493
494/// DBC row from SpellDescriptionVariables.db2: variable expressions used in spell text.
495#[derive(Clone, Debug, Deserialize, HasId)]
496#[allow(
497    non_snake_case,
498    reason = "fields mirror upstream DBC CSV headers exactly"
499)]
500pub struct SpellDescriptionVariablesRow {
501    pub ID: i32,
502    pub Variables: String,
503}
504
505/// DBC row from SpellXDescriptionVariables.db2: maps spells to their description variables.
506#[derive(Clone, Debug, Deserialize, HasFk)]
507#[allow(
508    non_snake_case,
509    reason = "fields mirror upstream DBC CSV headers exactly"
510)]
511#[fk_field = "SpellID"]
512pub struct SpellXDescriptionVariablesRow {
513    pub ID: i32,
514    pub SpellID: i32,
515    pub SpellDescriptionVariablesID: i32,
516}
517
518/// DBC row from SpellAuraOptions.db2: stacking, RPPM, and periodic timing.
519#[derive(Clone, Debug, Deserialize, HasFk)]
520#[allow(
521    non_snake_case,
522    reason = "fields mirror upstream DBC CSV headers exactly"
523)]
524#[fk_field = "SpellID"]
525pub struct SpellAuraOptionsRow {
526    pub ID: i32,
527    pub DifficultyID: i32,
528    pub CumulativeAura: i32,
529    pub ProcCategoryRecovery: i32,
530    pub ProcChance: i32,
531    pub ProcCharges: i32,
532    pub SpellProcsPerMinuteID: i32,
533    pub ProcTypeMask_0: i32,
534    pub ProcTypeMask_1: i32,
535    pub SpellID: i32,
536}
537
538/// DBC row from SpellProcsPerMinute.db2: base RPPM rate and flags.
539#[derive(Clone, Debug, Deserialize, HasId)]
540#[allow(
541    non_snake_case,
542    reason = "fields mirror upstream DBC CSV headers exactly"
543)]
544pub struct SpellProcsPerMinuteRow {
545    pub ID: i32,
546    pub BaseProcRate: f32,
547    pub Flags: i32,
548}
549
550/// DBC row from SpellProcsPerMinuteMod.db2: RPPM modifier by spec or coefficient.
551#[derive(Clone, Debug, Deserialize, HasFk)]
552#[allow(
553    non_snake_case,
554    reason = "fields mirror upstream DBC CSV headers exactly"
555)]
556#[fk_field = "SpellProcsPerMinuteID"]
557pub struct SpellProcsPerMinuteModRow {
558    pub ID: i32,
559    pub Type: i32,
560    pub Param: i32,
561    pub Coeff: f32,
562    pub SpellProcsPerMinuteID: i32,
563}
564
565/// DBC row from SpellLabel.db2: label tag assigned to a spell.
566#[derive(Clone, Debug, Deserialize, HasFk)]
567#[allow(
568    non_snake_case,
569    reason = "fields mirror upstream DBC CSV headers exactly"
570)]
571#[fk_field = "SpellID"]
572pub struct SpellLabelRow {
573    pub ID: i32,
574    pub LabelID: i32,
575    pub SpellID: i32,
576}
577
578#[cfg(test)]
579mod tests {
580    use googletest::prelude::*;
581
582    use super::*;
583
584    #[gtest]
585    fn attributes_vec_includes_every_available_field() -> Result<()> {
586        let row = SpellMiscRow {
587            ID: 1,
588            Attributes_0: 0,
589            Attributes_1: 1,
590            Attributes_2: 2,
591            Attributes_3: 3,
592            Attributes_4: 4,
593            Attributes_5: 5,
594            Attributes_6: 6,
595            Attributes_7: 7,
596            Attributes_8: 8,
597            Attributes_9: 9,
598            Attributes_10: 10,
599            Attributes_11: 11,
600            Attributes_12: 12,
601            Attributes_13: 13,
602            Attributes_14: 14,
603            Attributes_15: 15,
604            Attributes_16: 16,
605            DifficultyID: 0,
606            CastingTimeIndex: 0,
607            DurationIndex: 0,
608            PvPDurationIndex: 0,
609            RangeIndex: 0,
610            SchoolMask: 0,
611            Speed: 0.0,
612            LaunchDelay: 0.0,
613            MinDuration: 0.0,
614            SpellIconFileDataID: 0,
615            ActiveIconFileDataID: 0,
616            ContentTuningID: 0,
617            ShowFutureSpellPlayerConditionID: 0,
618            SpellVisualScript: 0,
619            ActiveSpellVisualScript: 0,
620            SpellID: 42,
621        };
622
623        let expected: Vec<i32> = (0..=16).collect();
624
625        verify_that!(row.attributes_vec(), eq(&expected))
626    }
627}