Skip to main content

wowlab_engine_combat/builder/combat_builder/
entities.rs

1use wowlab_engine_domain::rotation::to_simc_key;
2
3use super::CombatSystemBuilder;
4use crate::builder::{
5    AuraDefinitionDraft, AutoAttackDefinitionDraft, BuilderError, SpellDefinitionDraft,
6};
7
8impl CombatSystemBuilder {
9    /// Registers a spell definition.
10    ///
11    /// # Panics
12    ///
13    /// Panics if the validated aura registry exceeds the local-index capacity.
14    pub fn spell(
15        mut self,
16        name: &str,
17        id: u32,
18        f: impl FnOnce(SpellDefinitionDraft) -> Result<SpellDefinitionDraft, BuilderError>,
19    ) -> Self {
20        if self.pending_error.is_some() {
21            return self;
22        }
23
24        let mut builder = match f(SpellDefinitionDraft::new(name, id)) {
25            Ok(b) => b,
26            Err(e) => {
27                self.pending_error = Some(e);
28
29                return self;
30            }
31        };
32
33        let aura_ids = std::mem::take(&mut builder.applies_aura_ids);
34
35        for aura_id in aura_ids {
36            if let Some(pos) = self.aura_defs.iter().position(|a| a.aura_id == aura_id) {
37                builder = builder.applies_aura(crate::LocalAuraIdx::new(
38                    u8::try_from(pos).expect("aura count fits in u8"),
39                ));
40            } else {
41                self.pending_error = Some(crate::builder::BuilderErrorKind::GameData(
42                    // #t(rust_alloc_in_loop) allocation occurs only on the construction-failure path
43                    wowlab_engine_ports::EngineError::spec_construction(format!(
44                        "applies_aura_id: aura {aura_id} is not registered before spell {name} ({id})"
45                    )),
46                )
47                .into());
48
49                return self;
50            }
51        }
52
53        self.spell_ids.insert(name.to_string(), id);
54
55        if builder.cooldown_secs.is_some() {
56            let snake = to_simc_key(name);
57
58            self.hints.cooldown_spells.push(snake.clone());
59
60            if builder.max_charges > 0 {
61                self.hints.charged_spells.push(snake);
62            }
63        }
64
65        let hook = builder.hook;
66        let empower_release_hook = builder.empower_release_hook;
67        let tick_hook = builder.tick_hook;
68
69        self.spells.push(builder.finalize(name.to_string(), id));
70        self.cast_hooks.push(hook);
71        self.empower_release_hooks.push(empower_release_hook);
72        self.tick_hooks.push(tick_hook);
73
74        self
75    }
76
77    pub fn aura(
78        mut self,
79        name: &str,
80        id: u32,
81        f: impl FnOnce(AuraDefinitionDraft) -> Result<AuraDefinitionDraft, BuilderError>,
82    ) -> Self {
83        if self.pending_error.is_some() {
84            return self;
85        }
86
87        let builder = match f(AuraDefinitionDraft::new(name, id)) {
88            Ok(b) => b,
89            Err(e) => {
90                self.pending_error = Some(e);
91
92                return self;
93            }
94        };
95
96        if builder.on_tick.is_some() && builder.periodic.is_none() {
97            self.pending_error = Some(
98                crate::builder::BuilderErrorKind::GameData(
99                    wowlab_engine_ports::EngineError::spec_construction(format!(
100                        "aura {name} ({id}): tick_hook requires a registered periodic"
101                    )),
102                )
103                .into(),
104            );
105
106            return self;
107        }
108
109        self.aura_ids.insert(name.to_string(), id);
110
111        if builder.periodic.is_some() {
112            self.hints.dot_auras.push(to_simc_key(name));
113        }
114
115        self.aura_defs.push(builder.finalize());
116
117        self
118    }
119
120    pub fn auto_attack(
121        mut self,
122        f: impl FnOnce(AutoAttackDefinitionDraft) -> AutoAttackDefinitionDraft,
123    ) -> Self {
124        self.auto_attacks.push(f(AutoAttackDefinitionDraft::new()));
125
126        self
127    }
128}