Skip to main content

wowlab_parsers/parsers/transform/
creature.rs

1use wowlab_types::data::{
2    ContentTuningFlat, ContentTuningXDifficultyFlat, ContentTuningXExpectedFlat,
3    CreatureDifficultyFlat, CreatureFlat,
4};
5
6use crate::parsers::dbc::DbcData;
7
8/// Flatten every `Creature` row in the DBC dataset into `CreatureFlat` records.
9#[must_use]
10pub fn transform_all_creatures(dbc: &DbcData) -> Vec<CreatureFlat> {
11    dbc.creature
12        .values()
13        .map(|row| CreatureFlat {
14            id: row.ID,
15            name: row.Name_lang.clone().unwrap_or_default(),
16            classification: row.Classification,
17            creature_type: row.CreatureType,
18            creature_family: row.CreatureFamily,
19        })
20        .collect()
21}
22
23/// Flatten every `CreatureDifficulty` row in the DBC dataset into `CreatureDifficultyFlat` records.
24#[must_use]
25pub fn transform_all_creature_difficulties(dbc: &DbcData) -> Vec<CreatureDifficultyFlat> {
26    dbc.creature_difficulty
27        .values()
28        .flatten()
29        .map(|row| CreatureDifficultyFlat {
30            id: row.ID,
31            creature_id: row.CreatureID,
32            content_tuning_id: row.ContentTuningID,
33            faction_template_id: row.FactionTemplateID,
34            field_9_0_1_35522_003_min: row.Field_9_0_1_35522_003Min,
35            field_9_0_1_35522_003_max: row.Field_9_0_1_35522_003Max,
36            flags_0: row.Flags_0,
37            flags_1: row.Flags_1,
38            flags_2: row.Flags_2,
39            flags_3: row.Flags_3,
40            flags_4: row.Flags_4,
41            flags_5: row.Flags_5,
42            flags_6: row.Flags_6,
43            flags_7: row.Flags_7,
44            flags_8: row.Flags_8,
45        })
46        .collect()
47}
48
49/// Flatten every `ContentTuning` row in the DBC dataset into `ContentTuningFlat` records.
50#[must_use]
51pub fn transform_all_content_tunings(dbc: &DbcData) -> Vec<ContentTuningFlat> {
52    dbc.content_tuning
53        .values()
54        .map(|row| ContentTuningFlat {
55            id: row.ID,
56            flags: row.Flags,
57            expansion_id: row.ExpansionID,
58            hp_scaling_curve_id: row.HPScalingCurveID,
59            dmg_scaling_curve_id: row.DMGScalingCurveID,
60            hp_primary_stat_scaling_curve_id: row.HPPrimaryStatScalingCurveID,
61            dmg_primary_stat_scaling_curve_id: row.DMGPrimaryStatScalingCurveID,
62            primary_stat_scaling_mod_player_data_element_character_id: row
63                .PrimaryStatScalingModPlayerDataElementCharacterID,
64            primary_stat_scaling_mod_player_data_element_character_multiplier: row
65                .PrimaryStatScalingModPlayerDataElementCharacterMultiplier,
66            min_level_squish: row.MinLevelSquish,
67            max_level_squish: row.MaxLevelSquish,
68            min_level_scaling_offset: row.MinLevelScalingOffset,
69            max_level_scaling_offset: row.MaxLevelScalingOffset,
70            allowed_min_offset: row.AllowedMinOffset,
71            allowed_max_offset: row.AllowedMaxOffset,
72            lfg_min_level: row.LfgMinLevel,
73            lfg_max_level: row.LfgMaxLevel,
74            ilevel: row.ILevel,
75            xp_mult_quest: row.XpMultQuest,
76        })
77        .collect()
78}
79
80/// Flatten every `ContentTuningXDifficulty` row in the DBC dataset into `ContentTuningXDifficultyFlat` records.
81#[must_use]
82pub fn transform_all_content_tuning_x_difficulty(
83    dbc: &DbcData,
84) -> Vec<ContentTuningXDifficultyFlat> {
85    dbc.content_tuning_x_difficulty
86        .values()
87        .flatten()
88        .map(|row| ContentTuningXDifficultyFlat {
89            id: row.ID,
90            content_tuning_id: row.ContentTuningID,
91            difficulty_id: row.DifficultyID,
92        })
93        .collect()
94}
95
96/// Flatten every `ContentTuningXExpected` row in the DBC dataset into `ContentTuningXExpectedFlat` records.
97#[must_use]
98pub fn transform_all_content_tuning_x_expected(dbc: &DbcData) -> Vec<ContentTuningXExpectedFlat> {
99    dbc.content_tuning_x_expected
100        .values()
101        .flatten()
102        .map(|row| ContentTuningXExpectedFlat {
103            id: row.ID,
104            content_tuning_id: row.ContentTuningID,
105            expected_stat_mod_id: row.ExpectedStatModID,
106            min_mythic_plus_season_id: row.MinMythicPlusSeasonID,
107            max_mythic_plus_season_id: row.MaxMythicPlusSeasonID,
108        })
109        .collect()
110}
111
112#[cfg(test)]
113mod creature_tests {
114    use googletest::prelude::*;
115
116    use super::*;
117    use crate::parsers::{
118        dbc::rows::{CreatureDifficultyRow, CreatureRow},
119        transform::fixtures::dbc,
120    };
121
122    const fn creature_difficulty_row(
123        id: i32,
124        creature_id: i32,
125        content_tuning_id: i32,
126    ) -> CreatureDifficultyRow {
127        CreatureDifficultyRow {
128            ID: id,
129            Field_9_0_1_35522_003Min: 0,
130            Field_9_0_1_35522_003Max: 0,
131            FactionTemplateID: 16,
132            ContentTuningID: content_tuning_id,
133            Flags_0: 268_959_756,
134            Flags_1: 0,
135            Flags_2: 0,
136            Flags_3: 0,
137            Flags_4: 16_777_216,
138            Flags_5: 0,
139            Flags_6: 0,
140            Flags_7: 0,
141            Flags_8: 0,
142            CreatureID: creature_id,
143        }
144    }
145
146    #[gtest]
147    fn creatures_map_identity_and_classification() -> Result<()> {
148        let mut dbc = dbc();
149
150        dbc.creature.insert(
151            220_586,
152            CreatureRow {
153                ID: 220_586,
154                Name_lang: Some("Queen Ansurek".to_string()),
155                Classification: 1,
156                CreatureType: 7,
157                CreatureFamily: 0,
158            },
159        );
160        dbc.creature.insert(
161            3,
162            CreatureRow {
163                ID: 3,
164                Name_lang: None,
165                Classification: 0,
166                CreatureType: 6,
167                CreatureFamily: 0,
168            },
169        );
170
171        let result = transform_all_creatures(&dbc);
172
173        verify_that!(result, len(eq(2)))?;
174        verify_that!(
175            result,
176            contains(matches_pattern!(CreatureFlat {
177                id: eq(&220_586),
178                name: eq("Queen Ansurek"),
179                classification: eq(&1),
180                creature_type: eq(&7),
181                creature_family: eq(&0),
182            }))
183        )?;
184
185        verify_that!(
186            result,
187            contains(matches_pattern!(CreatureFlat {
188                id: eq(&3),
189                name: eq(""),
190                ..
191            }))
192        )
193    }
194
195    #[gtest]
196    fn creature_difficulties_flatten_grouped_rows() -> Result<()> {
197        let mut dbc = dbc();
198
199        dbc.creature_difficulty.insert(
200            220_586,
201            vec![creature_difficulty_row(270_199, 220_586, 719)],
202        );
203        dbc.creature_difficulty.insert(
204            3,
205            vec![
206                creature_difficulty_row(2, 3, 13),
207                creature_difficulty_row(4, 3, 14),
208            ],
209        );
210
211        let result = transform_all_creature_difficulties(&dbc);
212
213        verify_that!(result, len(eq(3)))?;
214
215        verify_that!(
216            result,
217            contains(matches_pattern!(CreatureDifficultyFlat {
218                id: eq(&270_199),
219                creature_id: eq(&220_586),
220                content_tuning_id: eq(&719),
221                faction_template_id: eq(&16),
222                flags_0: eq(&268_959_756),
223                flags_4: eq(&16_777_216),
224                ..
225            }))
226        )
227    }
228}
229
230#[cfg(test)]
231mod content_tuning_tests {
232    use googletest::prelude::*;
233
234    use super::*;
235    use crate::parsers::{
236        dbc::rows::{ContentTuningRow, ContentTuningXDifficultyRow, ContentTuningXExpectedRow},
237        transform::fixtures::dbc,
238    };
239
240    #[gtest]
241    fn content_tunings_map_all_scaling_fields() -> Result<()> {
242        let mut dbc = dbc();
243
244        dbc.content_tuning.insert(
245            719,
246            ContentTuningRow {
247                ID: 719,
248                Flags: 32,
249                ExpansionID: 7,
250                HPScalingCurveID: 0,
251                DMGScalingCurveID: 0,
252                HPPrimaryStatScalingCurveID: 0,
253                DMGPrimaryStatScalingCurveID: 0,
254                PrimaryStatScalingModPlayerDataElementCharacterID: 0,
255                PrimaryStatScalingModPlayerDataElementCharacterMultiplier: 0.0,
256                MinLevelSquish: 50,
257                MaxLevelSquish: 50,
258                MinLevelScalingOffset: 0,
259                MaxLevelScalingOffset: 0,
260                AllowedMinOffset: 0,
261                AllowedMaxOffset: 0,
262                LfgMinLevel: 0,
263                LfgMaxLevel: 0,
264                ILevel: 0,
265                XpMultQuest: 1.0,
266            },
267        );
268
269        let result = transform_all_content_tunings(&dbc);
270
271        verify_that!(
272            result,
273            elements_are![matches_pattern!(ContentTuningFlat {
274                id: eq(&719),
275                flags: eq(&32),
276                expansion_id: eq(&7),
277                hp_scaling_curve_id: eq(&0),
278                dmg_scaling_curve_id: eq(&0),
279                min_level_squish: eq(&50),
280                max_level_squish: eq(&50),
281                ..
282            })]
283        )?;
284
285        verify_that!(f64::from(result[0].xp_mult_quest), near(1.0, 1e-6))
286    }
287
288    #[gtest]
289    fn content_tuning_x_difficulty_flattens_mapping() -> Result<()> {
290        let mut dbc = dbc();
291
292        dbc.content_tuning_x_difficulty.insert(
293            719,
294            vec![ContentTuningXDifficultyRow {
295                ID: 4135,
296                DifficultyID: 14,
297                ContentTuningID: 719,
298            }],
299        );
300
301        let result = transform_all_content_tuning_x_difficulty(&dbc);
302
303        verify_that!(
304            result,
305            elements_are![matches_pattern!(ContentTuningXDifficultyFlat {
306                id: eq(&4135),
307                content_tuning_id: eq(&719),
308                difficulty_id: eq(&14),
309            })]
310        )
311    }
312
313    #[gtest]
314    fn content_tuning_x_expected_keeps_season_bounds() -> Result<()> {
315        let mut dbc = dbc();
316
317        dbc.content_tuning_x_expected.insert(
318            1279,
319            vec![
320                ContentTuningXExpectedRow {
321                    ID: 3032,
322                    ExpectedStatModID: 391,
323                    MinMythicPlusSeasonID: 117,
324                    MaxMythicPlusSeasonID: 0,
325                    ContentTuningID: 1279,
326                },
327                ContentTuningXExpectedRow {
328                    ID: 449,
329                    ExpectedStatModID: 14,
330                    MinMythicPlusSeasonID: 0,
331                    MaxMythicPlusSeasonID: 100,
332                    ContentTuningID: 1279,
333                },
334            ],
335        );
336
337        let result = transform_all_content_tuning_x_expected(&dbc);
338
339        verify_that!(result, len(eq(2)))?;
340        verify_that!(
341            result,
342            contains(matches_pattern!(ContentTuningXExpectedFlat {
343                id: eq(&3032),
344                content_tuning_id: eq(&1279),
345                expected_stat_mod_id: eq(&391),
346                min_mythic_plus_season_id: eq(&117),
347                max_mythic_plus_season_id: eq(&0),
348            }))
349        )?;
350
351        verify_that!(
352            result,
353            contains(matches_pattern!(ContentTuningXExpectedFlat {
354                id: eq(&449),
355                min_mythic_plus_season_id: eq(&0),
356                max_mythic_plus_season_id: eq(&100),
357                ..
358            }))
359        )
360    }
361}