wowlab_engine_combat/builder/combat_builder/
entities.rs1use wowlab_engine_domain::rotation::to_simc_key;
2
3use super::CombatSystemBuilder;
4use crate::builder::{
5 AuraDefinitionDraft, AutoAttackDefinitionDraft, BuilderError, SpellDefinitionDraft,
6};
7
8impl CombatSystemBuilder {
9 pub fn effect_identities(mut self, identities: &[crate::DamageActionId]) -> Self {
13 for identity in identities.iter().copied() {
14 if !self.generated_damage_identities.contains(&identity) {
15 self.generated_damage_identities.push(identity);
16 self.effect_spell_ids.push(identity.spell_id());
17 }
18 }
19
20 self
21 }
22
23 #[cfg(any(test, feature = "test-support"))]
27 #[doc(hidden)]
28 pub fn damage_identity(mut self, _name: &str, id: u32) -> Self {
29 if id != 0 {
30 self.damage_spell_ids.push(id);
31 self.generated_damage_identities
32 .push(crate::DamageActionId::new(0, id));
33 }
34
35 self
36 }
37
38 #[cfg(any(test, feature = "test-support"))]
40 #[doc(hidden)]
41 pub fn diagnostic_effect_identities(mut self, ids: &[u32]) -> Self {
42 wowlab_types::bench_metrics::record_keyed_lookup();
43
44 for &id in ids.iter().filter(|id| **id != 0) {
45 self.effect_spell_ids.push(id);
46 self.generated_damage_identities
47 .push(crate::DamageActionId::new(0, id));
48 }
49
50 self
51 }
52
53 pub fn spell(
59 mut self,
60 name: &str,
61 id: u32,
62 f: impl FnOnce(SpellDefinitionDraft) -> Result<SpellDefinitionDraft, BuilderError>,
63 ) -> Self {
64 if self.pending_error.is_some() {
65 return self;
66 }
67
68 let mut builder = match f(SpellDefinitionDraft::new(name, id)) {
69 Ok(b) => b,
70 Err(e) => {
71 self.pending_error = Some(e);
72
73 return self;
74 }
75 };
76
77 let aura_ids = std::mem::take(&mut builder.applies_aura_ids);
78
79 for aura_id in aura_ids {
80 if let Some(pos) = self.aura_defs.iter().position(|a| a.aura_id == aura_id) {
81 builder = builder.applies_aura(crate::LocalAuraIdx::new(
82 u8::try_from(pos).expect("aura count fits in u8"),
83 ));
84 } else {
85 self.pending_error = Some(crate::builder::BuilderErrorKind::GameData(
86 wowlab_engine_ports::EngineError::spec_construction(format!(
88 "applies_aura_id: aura {aura_id} is not registered before spell {name} ({id})"
89 )),
90 )
91 .into());
92
93 return self;
94 }
95 }
96
97 self.spell_ids.insert(name.to_string(), id);
98
99 if builder.cooldown_secs.is_some() {
100 let snake = to_simc_key(name);
101
102 self.hints.cooldown_spells.push(snake.clone());
103
104 if builder.max_charges > 0 {
105 self.hints.charged_spells.push(snake);
106 }
107 }
108
109 let hook = builder.hook;
110 let empower_release_hook = builder.empower_release_hook;
111 let tick_hook = builder.tick_hook;
112
113 self.spells.push(builder.finalize(name.to_string(), id));
114 self.cast_hooks.push(hook);
115 self.empower_release_hooks.push(empower_release_hook);
116 self.tick_hooks.push(tick_hook);
117
118 self
119 }
120
121 pub fn aura(
122 mut self,
123 name: &str,
124 id: u32,
125 f: impl FnOnce(AuraDefinitionDraft) -> Result<AuraDefinitionDraft, BuilderError>,
126 ) -> Self {
127 if self.pending_error.is_some() {
128 return self;
129 }
130
131 let builder = match f(AuraDefinitionDraft::new(name, id)) {
132 Ok(b) => b,
133 Err(e) => {
134 self.pending_error = Some(e);
135
136 return self;
137 }
138 };
139
140 if builder.on_tick.is_some() && builder.periodic.is_none() {
141 self.pending_error = Some(
142 crate::builder::BuilderErrorKind::GameData(
143 wowlab_engine_ports::EngineError::spec_construction(format!(
144 "aura {name} ({id}): tick_hook requires a registered periodic"
145 )),
146 )
147 .into(),
148 );
149
150 return self;
151 }
152
153 self.aura_ids.insert(name.to_string(), id);
154
155 if builder.periodic.is_some() {
156 self.hints.dot_auras.push(to_simc_key(name));
157 }
158
159 self.aura_defs.push(builder.finalize());
160
161 self
162 }
163
164 pub fn auto_attack(
165 mut self,
166 f: impl FnOnce(AutoAttackDefinitionDraft) -> AutoAttackDefinitionDraft,
167 ) -> Self {
168 self.auto_attacks.push(f(AutoAttackDefinitionDraft::new()));
169
170 self
171 }
172}