1use serde::{Deserialize, Serialize};
2use wowlab_engine_macros::CopyInsert;
3
4#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
5#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
6#[serde(rename_all = "lowercase")]
7#[non_exhaustive]
8pub enum InstanceKind {
9 Raid,
10 Dungeon,
11 #[default]
12 World,
13}
14
15impl InstanceKind {
16 #[must_use]
17 pub const fn as_str(&self) -> &'static str {
18 match self {
19 Self::Raid => "raid",
20 Self::Dungeon => "dungeon",
21 Self::World => "world",
22 }
23 }
24}
25
26#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
28#[serde(rename_all = "lowercase")]
29#[non_exhaustive]
30pub enum DifficultyKind {
31 Raid,
32 Dungeon,
33 Pvp,
34 World,
35 Scenario,
36 #[default]
37 Unknown,
38}
39
40impl DifficultyKind {
41 #[must_use]
42 pub const fn as_str(&self) -> &'static str {
43 match self {
44 Self::Raid => "raid",
45 Self::Dungeon => "dungeon",
46 Self::Pvp => "pvp",
47 Self::World => "world",
48 Self::Scenario => "scenario",
49 Self::Unknown => "unknown",
50 }
51 }
52}
53
54#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
55#[serde(rename_all = "lowercase")]
56#[non_exhaustive]
57pub enum DropSourceKind {
58 #[default]
59 Raid,
60 Dungeon,
61}
62
63impl DropSourceKind {
64 #[must_use]
65 pub const fn as_str(&self) -> &'static str {
66 match self {
67 Self::Raid => "raid",
68 Self::Dungeon => "dungeon",
69 }
70 }
71}
72
73#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
75#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
76#[non_exhaustive]
77pub enum DifficultyKey {
78 #[serde(rename = "LFR")]
79 Lfr,
80 #[default]
81 Normal,
82 Heroic,
83 Mythic,
84 #[serde(rename = "MythicPlus_2_3")]
85 MythicPlusTwoThree,
86 #[serde(rename = "MythicPlus_4")]
87 MythicPlusFour,
88 #[serde(rename = "MythicPlus_5")]
89 MythicPlusFive,
90 #[serde(rename = "MythicPlus_6_7")]
91 MythicPlusSixSeven,
92 #[serde(rename = "MythicPlus_8_9")]
93 MythicPlusEightNine,
94 #[serde(rename = "MythicPlus_10Plus")]
95 MythicPlusTenPlus,
96 #[serde(rename = "MythicPlus_VaultBR")]
97 MythicPlusVaultBr,
98}
99
100impl DifficultyKey {
101 #[must_use]
102 pub const fn as_str(&self) -> &'static str {
103 match self {
104 Self::Lfr => "LFR",
105 Self::Normal => "Normal",
106 Self::Heroic => "Heroic",
107 Self::Mythic => "Mythic",
108 Self::MythicPlusTwoThree => "MythicPlus_2_3",
109 Self::MythicPlusFour => "MythicPlus_4",
110 Self::MythicPlusFive => "MythicPlus_5",
111 Self::MythicPlusSixSeven => "MythicPlus_6_7",
112 Self::MythicPlusEightNine => "MythicPlus_8_9",
113 Self::MythicPlusTenPlus => "MythicPlus_10Plus",
114 Self::MythicPlusVaultBr => "MythicPlus_VaultBR",
115 }
116 }
117
118 #[must_use]
120 pub const fn order(self) -> u8 {
121 match self {
122 Self::Lfr => 0,
123 Self::Normal => 1,
124 Self::Heroic => 2,
125 Self::Mythic => 3,
126 Self::MythicPlusTwoThree => 4,
127 Self::MythicPlusFour => 5,
128 Self::MythicPlusFive => 6,
129 Self::MythicPlusSixSeven => 7,
130 Self::MythicPlusEightNine => 8,
131 Self::MythicPlusTenPlus => 9,
132 Self::MythicPlusVaultBr => 10,
133 }
134 }
135}
136
137#[derive(Clone, Debug, Default, Deserialize, Serialize)]
138#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
139pub struct TierInstanceEntry {
140 pub instance_id: i32,
141 pub instance_name: String,
142 pub kind: InstanceKind,
143 pub order_index: i32,
144 pub map_id: i32,
145 pub is_mythic_plus: bool,
146}
147
148#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
149pub struct JournalTierFlat {
150 pub id: i32,
151 pub name: String,
152 pub expansion: i32,
153 pub player_condition_id: i32,
154 #[copy(json)]
155 pub instances: Vec<TierInstanceEntry>,
156}
157
158#[derive(Clone, Debug, Default, Deserialize, Serialize)]
159#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
160pub struct EncounterEntry {
161 pub id: i32,
162 pub name: String,
163 pub order_index: i32,
164 pub dungeon_encounter_id: i32,
165 pub ui_map_id: i32,
166 pub image_file_data_id: i32,
167 pub image_storage_path: String,
168 pub creature_display_info_id: i32,
169 pub ui_model_scene_id: i32,
170}
171
172#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
173pub struct JournalInstanceFlat {
174 pub id: i32,
175 pub name: String,
176 pub description: String,
177 pub map_id: i32,
178 pub background_file_data_id: i32,
179 pub background_storage_path: String,
180 pub button_file_data_id: i32,
181 pub button_storage_path: String,
182 pub button_small_file_data_id: i32,
183 pub lore_file_data_id: i32,
184 pub lore_storage_path: String,
185 pub loadscreen_file_data_id: i32,
186 pub loadscreen_storage_path: String,
187 pub loadscreen_seo_storage_path: String,
188 #[copy(enum_str)]
189 pub kind: InstanceKind,
190 pub is_mythic_plus: bool,
191 pub is_current_season: bool,
192 pub tier_id: i32,
193 pub tier_name: String,
194 pub tier_order_index: i32,
195 #[copy(json)]
196 pub encounters: Vec<EncounterEntry>,
197}
198
199#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
200#[copy_insert(patch_first)]
201pub struct CraftedProductFlat {
202 #[copy(skip)]
204 pub id: i64,
205 pub crafting_data_id: i32,
206 pub crafted_item_id: i32,
207 pub crafted_item_name: String,
208 pub quality_tier: i32,
209 pub quality_item_id: i32,
210 pub quality_item_name: String,
211 pub quality_item_level: i32,
212 pub profession_id: i32,
213 pub profession_name: String,
214 pub profession_enum_value: i32,
215 pub item_bonus_tree_id: i32,
216}
217
218#[derive(Clone, Debug, Default, Deserialize, Serialize)]
219#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
220pub struct PvpTierRewardEntry {
221 pub tier_index: i32,
222 pub tier_name: String,
223 pub min_rating: i32,
224 pub max_rating: i32,
225 pub bracket_id: i32,
226 pub end_of_match_ilvl: i32,
227 pub weekly_cache_ilvl: i32,
228}
229
230#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
232pub struct PvpSeasonFlat {
233 pub id: i32,
234 pub milestone_season: i32,
235 pub alliance_achievement_id: i32,
236 pub horde_achievement_id: i32,
237 #[copy(json)]
238 pub tier_rewards: Vec<PvpTierRewardEntry>,
239}
240
241#[derive(Clone, Debug, Default, Deserialize, Serialize)]
242#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
243pub struct KeyLevelRewardEntry {
244 pub key_level: i32,
245 pub activity_tier_id: i32,
246 pub weekly_reward_level: i32,
247 pub end_of_run_reward_level: i32,
248}
249
250#[derive(Clone, Debug, Default, Deserialize, Serialize)]
251#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
252pub struct TrackedDungeonEntry {
253 pub map_challenge_mode_id: i32,
254 pub map_id: i32,
255 pub instance_name: String,
256}
257
258#[derive(Clone, Debug, Default, Deserialize, Serialize)]
259#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
260pub struct TrackedAffixEntry {
261 pub keystone_affix_id: i32,
262 pub bonus_rating: i32,
263}
264
265#[derive(Clone, Debug, Default, Deserialize, Serialize)]
266#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
267pub struct KeyFloorEntry {
268 pub key_floor: i32,
269 pub player_condition_id: i32,
270}
271
272#[derive(Clone, Debug, Default, Deserialize, Serialize)]
274pub struct MythicPlusSeasonFlat {
275 pub id: i32,
276 pub milestone_season: i32,
277 pub start_time_event: i32,
278 pub expansion_level: i32,
279 pub heroic_lfg_dungeon_min_gear: i32,
280 pub display_season_id: i32,
281 pub display_season_name: String,
282 pub display_season_index: i32,
283 pub delves_season_id: i32,
284 pub item_group_ilvl_scaling_id: i32,
285 pub bonus_list_groups: Vec<i32>,
286 pub crest_currencies: Vec<i32>,
287 pub valorstones_currency_id: i32,
288 pub tracked_dungeons: Vec<TrackedDungeonEntry>,
289 pub tracked_affixes: Vec<TrackedAffixEntry>,
290 pub key_floors: Vec<KeyFloorEntry>,
291 pub key_rewards: Vec<KeyLevelRewardEntry>,
292}
293
294#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
296pub struct Currency {
297 pub id: i32,
298 pub name: String,
299 pub category_id: i32,
300 pub category_name: String,
301 pub icon_file_id: i32,
302 pub max_qty: i32,
303 pub max_earnable_per_week: i32,
304 pub quality: i32,
305 pub order_index: i32,
306 pub flags: i32,
307}
308
309#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
310pub struct CurrencyCategory {
311 pub id: i32,
312 pub name: String,
313 pub flags: i32,
314 pub expansion_id: i32,
315 pub parent_category_id: i32,
316}
317
318#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
319pub struct ItemBonusListGroup {
320 pub id: i32,
321 pub sequence_spell_id: i32,
322 pub player_condition_id: i32,
323 pub item_extended_cost_id: i32,
324 pub item_logical_cost_group_id: i32,
325 pub item_group_ilvl_scaling_id: i32,
326}
327
328#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
329pub struct ItemGroupIlvlScalingEntry {
330 pub id: i32,
331 pub currency_type_id: i32,
332 pub item_id: i32,
333 pub cost_scaling: f32,
334 pub flags: i32,
335 pub player_condition_id: i32,
336 pub conditional_cost_scaling: f32,
337 pub item_group_ilvl_scaling_id: i32,
338}
339
340#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
341pub struct ItemLevelSelector {
342 pub id: i32,
343 pub min_item_level: i32,
344 pub item_level_selector_quality_set_id: i32,
345 pub azerite_unlock_mapping_set_id: i32,
346}
347
348#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
349pub struct ItemLevelSelectorQualitySet {
350 pub id: i32,
351 pub ilvl_rare: i32,
352 pub ilvl_epic: i32,
353}
354
355#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
356pub struct ItemLevelSelectorQuality {
357 pub id: i32,
358 pub quality_item_bonus_list_id: i32,
359 pub quality: i32,
360 pub parent_ils_quality_set_id: i32,
361}
362
363#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
364pub struct ItemConversionFlat {
365 pub id: i32,
366 pub item_bonus_tree_id: i32,
367 pub item_logical_cost_group_id: i32,
368 pub alternate_item_logical_cost_group_id: i32,
369 pub player_condition_id: i32,
370 pub items: Vec<i32>,
371}
372
373#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
374pub struct ChallengeModeItemBonusOverride {
375 pub id: i32,
376 pub item_bonus_tree_group_id: i32,
377 pub dst_item_bonus_tree_id: i32,
378 pub value: i32,
379 pub required_time_event_passed: i32,
380 pub required_time_event_not_passed: i32,
381 pub src_item_bonus_tree_id: i32,
382}
383
384#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
385pub struct ItemBonusSequenceSpell {
386 pub id: i32,
387 pub spell_id: i32,
388 pub item_id: i32,
389}
390
391#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
392pub struct DelvesSeason {
393 pub id: i32,
394 pub faction_id: i32,
395}
396
397#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
399#[copy_insert(patch_first)]
400pub struct ItemDropScalingFlat {
401 #[copy(skip)]
403 pub id: i64,
404 pub item_id: i32,
405 #[copy(enum_str)]
406 pub source_kind: DropSourceKind,
407 #[copy(enum_str)]
408 pub difficulty_key: DifficultyKey,
409 pub upgrade_id: i32,
411 pub flair_id: i32,
413}
414
415#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
416pub struct PvpTier {
417 pub id: i32,
418 pub name: String,
419 pub min_rating: i32,
420 pub max_rating: i32,
421 pub prev_tier: i32,
422 pub next_tier: i32,
423 pub bracket_id: i32,
424 pub rank: i32,
425 pub rank_icon: i32,
426}
427
428#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
430pub struct Difficulty {
431 pub id: i32,
432 pub name: String,
433 pub instance_type: i32,
434 pub order_index: i32,
435 pub min_players: i32,
436 pub max_players: i32,
437 pub flags: i32,
438 pub item_context: i32,
439 #[copy(enum_str)]
440 pub kind: DifficultyKind,
441}
442
443#[derive(Clone, CopyInsert, Debug, Default, Deserialize, Serialize)]
445pub struct Profession {
446 pub id: i32,
447 pub skill_line_id: i32,
448 pub profession_enum_value: i32,
449 pub name: String,
450}
451
452#[cfg(test)]
453mod tests {
454 use googletest::prelude::*;
455 use rstest::rstest;
456
457 use super::*;
458
459 #[gtest]
460 #[rstest]
461 #[case::raid(InstanceKind::Raid, "raid")]
462 #[case::dungeon(InstanceKind::Dungeon, "dungeon")]
463 #[case::world(InstanceKind::World, "world")]
464 fn instance_kind_as_str(#[case] kind: InstanceKind, #[case] expected: &str) -> Result<()> {
465 verify_that!(kind.as_str(), eq(expected))
466 }
467
468 #[gtest]
469 #[rstest]
470 #[case::raid(DifficultyKind::Raid, "raid")]
471 #[case::dungeon(DifficultyKind::Dungeon, "dungeon")]
472 #[case::pvp(DifficultyKind::Pvp, "pvp")]
473 #[case::world(DifficultyKind::World, "world")]
474 #[case::scenario(DifficultyKind::Scenario, "scenario")]
475 #[case::unknown(DifficultyKind::Unknown, "unknown")]
476 fn difficulty_kind_as_str(#[case] kind: DifficultyKind, #[case] expected: &str) -> Result<()> {
477 verify_that!(kind.as_str(), eq(expected))
478 }
479
480 #[gtest]
481 #[rstest]
482 #[case::raid(DropSourceKind::Raid, "raid")]
483 #[case::dungeon(DropSourceKind::Dungeon, "dungeon")]
484 fn drop_source_kind_as_str(#[case] kind: DropSourceKind, #[case] expected: &str) -> Result<()> {
485 verify_that!(kind.as_str(), eq(expected))
486 }
487
488 #[gtest]
489 #[rstest]
490 #[case::lfr(DifficultyKey::Lfr, "LFR")]
491 #[case::normal(DifficultyKey::Normal, "Normal")]
492 #[case::heroic(DifficultyKey::Heroic, "Heroic")]
493 #[case::mythic(DifficultyKey::Mythic, "Mythic")]
494 #[case::mplus_2_3(DifficultyKey::MythicPlusTwoThree, "MythicPlus_2_3")]
495 #[case::mplus_4(DifficultyKey::MythicPlusFour, "MythicPlus_4")]
496 #[case::mplus_5(DifficultyKey::MythicPlusFive, "MythicPlus_5")]
497 #[case::mplus_6_7(DifficultyKey::MythicPlusSixSeven, "MythicPlus_6_7")]
498 #[case::mplus_8_9(DifficultyKey::MythicPlusEightNine, "MythicPlus_8_9")]
499 #[case::mplus_10plus(DifficultyKey::MythicPlusTenPlus, "MythicPlus_10Plus")]
500 #[case::mplus_vault_br(DifficultyKey::MythicPlusVaultBr, "MythicPlus_VaultBR")]
501 fn difficulty_key_as_str(#[case] key: DifficultyKey, #[case] expected: &str) -> Result<()> {
502 verify_that!(key.as_str(), eq(expected))
503 }
504
505 #[gtest]
506 #[rstest]
507 #[case::lfr(DifficultyKey::Lfr, 0)]
508 #[case::normal(DifficultyKey::Normal, 1)]
509 #[case::heroic(DifficultyKey::Heroic, 2)]
510 #[case::mythic(DifficultyKey::Mythic, 3)]
511 #[case::mplus_2_3(DifficultyKey::MythicPlusTwoThree, 4)]
512 #[case::mplus_4(DifficultyKey::MythicPlusFour, 5)]
513 #[case::mplus_5(DifficultyKey::MythicPlusFive, 6)]
514 #[case::mplus_6_7(DifficultyKey::MythicPlusSixSeven, 7)]
515 #[case::mplus_8_9(DifficultyKey::MythicPlusEightNine, 8)]
516 #[case::mplus_10plus(DifficultyKey::MythicPlusTenPlus, 9)]
517 #[case::mplus_vault_br(DifficultyKey::MythicPlusVaultBr, 10)]
518 fn difficulty_key_order(#[case] key: DifficultyKey, #[case] expected: u8) -> Result<()> {
519 verify_that!(key.order(), eq(expected))
520 }
521
522 #[gtest]
523 fn difficulty_key_order_strictly_increasing() -> Result<()> {
524 let ordered = [
525 DifficultyKey::Lfr,
526 DifficultyKey::Normal,
527 DifficultyKey::Heroic,
528 DifficultyKey::Mythic,
529 DifficultyKey::MythicPlusTwoThree,
530 DifficultyKey::MythicPlusFour,
531 DifficultyKey::MythicPlusFive,
532 DifficultyKey::MythicPlusSixSeven,
533 DifficultyKey::MythicPlusEightNine,
534 DifficultyKey::MythicPlusTenPlus,
535 DifficultyKey::MythicPlusVaultBr,
536 ];
537
538 for pair in ordered.windows(2) {
539 verify_that!(pair[0].order() < pair[1].order(), eq(true))?;
540 }
541
542 Ok(())
543 }
544
545 #[gtest]
546 fn difficulty_key_serialize_rename() -> Result<()> {
547 verify_that!(
548 serde_json::to_string(&DifficultyKey::MythicPlusVaultBr).ok(),
549 some(eq("\"MythicPlus_VaultBR\""))
550 )
551 }
552
553 #[gtest]
554 #[rstest]
555 #[case::lfr("\"LFR\"", DifficultyKey::Lfr)]
556 #[case::mplus_2_3("\"MythicPlus_2_3\"", DifficultyKey::MythicPlusTwoThree)]
557 #[case::mplus_vault_br("\"MythicPlus_VaultBR\"", DifficultyKey::MythicPlusVaultBr)]
558 fn difficulty_key_deserialize_rename(
559 #[case] wire: &str,
560 #[case] expected: DifficultyKey,
561 ) -> Result<()> {
562 verify_that!(
563 serde_json::from_str::<DifficultyKey>(wire).ok(),
564 some(eq(expected))
565 )
566 }
567}