Skip to main content

wowlab_engine_content/
items.rs

1use wowlab_engine_combat::CombatSystemBuilder;
2use wowlab_engine_domain::dbc::resolve_item_budget;
3use wowlab_engine_gamedata::{ItemBudget, ResolvedGameData, rating_multiplier_slot_for_gear};
4use wowlab_engine_ports::{EngineError, EquippedItem};
5
6macro_rules! register_item {
7    (
8        $builder:expr, $data:expr, $display:expr
9        $(, rppm: ($item_id:expr, $ppm:expr, $haste:expr))?
10        $(, rppm_driver: ($driver_item_id:expr, $driver_spell_id:expr))?
11        $(, auras: ($budget:expr) [ $( ($aura_const:expr, $aura_fn:ident) ),* $(,)? ])?
12        $(, spells: [ $( ($spell_const:expr, $spell_fn:ident) ),* $(,)? ])?
13        $(, precombat: [ $( $precombat_const:expr ),* $(,)? ])?
14        $(,)?
15    ) => {{
16        let data = $data;
17        let display = $display;
18        let builder = $builder;
19        $( let builder = builder.register_item_rppm($item_id, $ppm, $haste); )?
20        $( let builder = builder.register_item_rppm_from_data($driver_item_id, $driver_spell_id); )?
21        $(
22            let budget = $budget;
23            $( let builder = builder.aura(display, $aura_const, |a| $aura_fn(a, data, budget)); )*
24        )?
25        $($( let builder = builder.spell(display, $spell_const, |s| $spell_fn(s, data)); )*)?
26        $($( let builder = builder.precombat_aura_id($precombat_const); )*)?
27        builder
28    }};
29}
30
31pub(crate) use register_item;
32
33pub(crate) fn item_budget(
34    data: &ResolvedGameData,
35    item: &EquippedItem,
36) -> Result<ItemBudget, EngineError> {
37    let slot = rating_multiplier_slot_for_gear(item.slot);
38
39    if let Some(budget) =
40        resolve_item_budget(data.game_tables(), item.item_level, item.quality, slot)
41    {
42        return Ok(budget);
43    }
44
45    if data.is_empty() {
46        return Ok(ItemBudget::default());
47    }
48
49    Err(EngineError::spec_construction(format!(
50        "missing item budget for equipped item {} at item level {} with quality {}",
51        item.item_id, item.item_level, item.quality
52    )))
53}
54
55pub(crate) fn register_simple_equip_auras(
56    data: &ResolvedGameData,
57    display: &str,
58    aura_ids: &[u32],
59    create_builder: impl FnOnce() -> CombatSystemBuilder,
60) -> CombatSystemBuilder {
61    let mut builder = create_builder();
62
63    for &aura_id in aura_ids {
64        builder = builder.aura(display, aura_id, |aura| {
65            let spell = wowlab_types::sim::SpellIdx::from_raw(aura_id);
66            let duration = data.aura_duration_ms(spell).ok_or_else(|| {
67                wowlab_engine_combat::BuilderError::missing_spell_data(aura_id, "aura_duration_ms")
68            })?;
69            let max_stacks = data.aura_max_stacks(spell).ok_or_else(|| {
70                wowlab_engine_combat::BuilderError::missing_spell_data(aura_id, "aura_max_stacks")
71            })?;
72
73            Ok(aura
74                .on_player()
75                .duration_ms(duration)
76                .max_stacks(max_stacks))
77        });
78    }
79
80    builder
81}
82
83#[cfg(test)]
84#[path = "items/arcanoweave_cord_tests.rs"]
85mod arcanoweave_cord_tests;
86
87#[cfg(test)]
88#[path = "items/item_budget_tests.rs"]
89mod item_budget_tests;
90
91#[cfg(test)]
92#[path = "items/registration_tests.rs"]
93mod registration_tests;
94
95#[cfg(test)]
96#[path = "items/rotbloom_tests.rs"]
97mod rotbloom_tests;