wowlab_engine_gamedata/game_data/
builder.rs1use std::sync::Arc;
6
7use wowlab_types::{
8 data::WeaponStats,
9 sim::{EffectRef, SpellIdx},
10};
11
12use super::{
13 AuraProps, ConsumableSpells, PassiveSpellModifiers, PowerTypeProps, ResolvedCurves,
14 ResolvedGameData, ResolvedGameDataInner, ResolvedGameTables, SpellProps,
15};
16
17#[derive(Debug)]
19pub struct ResolvedGameDataBuilder {
20 inner: ResolvedGameDataInner,
21}
22
23macro_rules! effect_insert {
24 (effect $ty:ty; $($method:ident => $field:ident),+ $(,)?) => {
25 $(
26 pub fn $method(&mut self, effect: EffectRef, value: $ty) {
27 self.inner
28 .effects.values
29 .entry(effect)
30 .or_default()
31 .$field = value;
32 }
33 )+
34 };
35 (parts $ty:ty; $($method:ident => $field:ident),+ $(,)?) => {
36 $(
37 pub fn $method(&mut self, spell_id: SpellIdx, effect_index: u8, value: $ty) {
38 self.inner
39 .effects.values
40 .entry(EffectRef::new(spell_id, effect_index))
41 .or_default()
42 .$field = value;
43 }
44 )+
45 };
46}
47
48impl ResolvedGameDataBuilder {
49 pub(crate) fn new() -> Self {
50 Self {
51 inner: ResolvedGameDataInner::default(),
52 }
53 }
54
55 pub fn insert_implicit_targets(&mut self, effect: EffectRef, target_a: i32, target_b: i32) {
56 let values = self.inner.effects.values.entry(effect).or_default();
57
58 values.implicit_target_a = target_a;
59 values.implicit_target_b = target_b;
60 }
61
62 pub fn insert_effect_class_mask(
64 &mut self,
65 spell_id: SpellIdx,
66 effect_index: u8,
67 mask: [i32; super::CLASS_FAMILY_FLAGS],
68 ) {
69 self.inner
70 .effects
71 .values
72 .entry(EffectRef::new(spell_id, effect_index))
73 .or_default()
74 .class_mask = mask;
75 }
76
77 pub fn insert_school_mask(&mut self, spell_id: SpellIdx, school_mask: i32) {
78 self.inner
79 .effects
80 .school_masks
81 .insert(spell_id, school_mask);
82 }
83
84 pub fn insert_spell_props(&mut self, spell_id: SpellIdx, props: SpellProps) {
85 self.inner
86 .spells
87 .props
88 .insert(spell_id, super::normalize_spell_modifier_defaults(props));
89 }
90
91 pub fn insert_server_proc_attributes(&mut self, spell_id: SpellIdx, attributes: u32) -> bool {
93 let Some(props) = self.inner.spells.props.get_mut(&spell_id) else {
94 return false;
95 };
96
97 props.proc_attributes = attributes;
98
99 true
100 }
101
102 pub fn insert_aura_props(&mut self, spell_id: SpellIdx, props: AuraProps) {
103 self.inner.spells.aura_props.insert(spell_id, props);
104 }
105
106 pub fn insert_power_type(&mut self, type_id: i32, props: PowerTypeProps) {
107 self.inner.spells.power_types.insert(type_id, props);
108 }
109
110 pub fn power_type_mut(&mut self, type_id: i32) -> Option<&mut PowerTypeProps> {
112 self.inner.spells.power_types.get_mut(&type_id)
113 }
114
115 pub fn insert_spell_override(&mut self, overridden_id: SpellIdx, replacement_id: SpellIdx) {
116 self.inner
117 .spells
118 .overrides
119 .insert(overridden_id, replacement_id);
120 }
121
122 pub fn insert_spell_learn(&mut self, teacher_id: SpellIdx, learned_id: SpellIdx) {
123 let learned = self.inner.spells.learns.entry(teacher_id).or_default();
124
125 if !learned.contains(&learned_id) {
126 learned.push(learned_id);
127 }
128 }
129
130 pub fn insert_spell_labels(&mut self, spell_id: SpellIdx, labels: Vec<i32>) {
132 self.inner.spells.labels.insert(spell_id, labels);
133 }
134
135 pub fn insert_spell_class_flags(
137 &mut self,
138 spell_id: SpellIdx,
139 class_set: i32,
140 class_mask: [i32; super::CLASS_FAMILY_FLAGS],
141 ) {
142 self.inner.spells.class_flags.insert(
143 spell_id,
144 super::SpellClassFlags {
145 class_set,
146 class_mask,
147 },
148 );
149 }
150
151 pub fn set_armor_constant(&mut self, value: f64) {
152 self.inner.environment.armor_constant = value;
153 }
154
155 pub fn set_creature_armor(&mut self, value: f64) {
156 self.inner.environment.creature_armor = value;
157 }
158
159 pub fn set_target_armor_mult(&mut self, value: f64) {
160 self.inner.environment.target_armor_mult = value;
161 }
162
163 pub fn set_armor_constant_mod(&mut self, value: f64) {
164 self.inner.environment.armor_constant_mod = value;
165 }
166
167 pub fn set_main_hand(&mut self, value: WeaponStats) {
168 self.inner.environment.main_hand = value;
169 }
170
171 pub fn set_off_hand(&mut self, value: Option<WeaponStats>) {
172 self.inner.environment.off_hand = value;
173 }
174
175 pub fn set_curves(&mut self, value: ResolvedCurves) {
176 self.inner.scaling.curves = value;
177 }
178
179 pub fn set_game_tables(&mut self, value: ResolvedGameTables) {
180 self.inner.scaling.game_tables = value;
181 }
182
183 pub fn set_level(&mut self, value: u32) {
184 self.inner.scaling.level = value;
185 }
186
187 pub fn set_auto_attack_damage_mult(&mut self, value: f64) {
188 self.inner.passives.auto_attack_damage_mult = value;
189 }
190
191 pub fn set_pet_damage_mult(&mut self, value: f64) {
192 self.inner.passives.pet_damage_mult = value;
193 }
194
195 pub fn set_guardian_damage_mult(&mut self, value: f64) {
196 self.inner.passives.guardian_damage_mult = value;
197 }
198
199 pub fn set_specialization_spells(&mut self, value: Vec<SpellIdx>) {
200 self.inner.spells.specialization = value;
201 }
202
203 pub fn set_folded_passive_spells(&mut self, value: Vec<SpellIdx>) {
204 self.inner.passives.folded_spells = value;
205 }
206
207 pub fn set_class_target_debuffs(&mut self, value: Vec<SpellIdx>) {
208 self.inner.passives.class_target_debuffs = value;
209 }
210
211 pub fn set_consumable_spells(&mut self, value: ConsumableSpells) {
212 self.inner.environment.consumables = value;
213 }
214
215 pub fn insert_passive_spell_modifiers(
217 &mut self,
218 spell_id: SpellIdx,
219 modifiers: PassiveSpellModifiers,
220 ) {
221 self.inner
222 .passives
223 .spell_modifiers
224 .insert(spell_id, modifiers);
225 }
226
227 pub fn update_passive_spell_modifiers(
229 &mut self,
230 spell_id: SpellIdx,
231 update: impl FnOnce(&mut PassiveSpellModifiers),
232 ) {
233 update(
234 self.inner
235 .passives
236 .spell_modifiers
237 .entry(spell_id)
238 .or_default(),
239 );
240 }
241
242 pub fn insert_pet_stat_inheritance_mult(
244 &mut self,
245 npc_id: u32,
246 attack_power_mult: f64,
247 spell_power_mult: f64,
248 ) {
249 self.inner
250 .passives
251 .pet_attack_power_inheritance
252 .insert(npc_id, attack_power_mult);
253 self.inner
254 .passives
255 .pet_spell_power_inheritance
256 .insert(npc_id, spell_power_mult);
257 }
258
259 pub fn push_crit_damage_per_crit_chance(&mut self, school_mask: i32, percent: f64) {
261 self.inner
262 .passives
263 .crit_damage_per_crit_chance
264 .push((school_mask, percent));
265 }
266
267 #[must_use]
268 pub fn build(mut self) -> ResolvedGameData {
269 for effect in self.inner.effects.values.keys() {
270 self.inner
271 .effects
272 .max_effect_index_by_spell
273 .entry(effect.spell)
274 .and_modify(|maximum| *maximum = (*maximum).max(effect.effect_index))
275 .or_insert(effect.effect_index);
276 }
277
278 for (effect, values) in &mut self.inner.effects.values {
279 values.source_class_set = self
280 .inner
281 .spells
282 .class_flags
283 .get(&effect.spell)
284 .map_or(0, |flags| flags.class_set);
285 }
286
287 ResolvedGameData {
288 inner: Arc::new(self.inner),
289 }
290 }
291
292 effect_insert! {
293 effect i32;
294 insert_scaling_class => scaling_class,
295 insert_chain_targets => chain_targets,
296 insert_effect_type => effect_type,
297 insert_effect_mechanic => effect_mechanic,
298 insert_effect_attributes => effect_attributes,
299 insert_effect_aura => effect_aura,
300 insert_trigger_spell => trigger_spell,
301 insert_effect_misc_value_0 => misc_value_0,
302 insert_effect_misc_value_1 => misc_value_1,
303 insert_shapeshift_form_flags => shapeshift_form_flags,
304 }
305
306 effect_insert! {
307 parts i32;
308 insert_shapeshift_combat_round_time_ms => shapeshift_combat_round_time_ms,
309 }
310
311 effect_insert! {
312 parts f64;
313 insert_ap_coef => ap_coef,
314 insert_sp_coef => sp_coef,
315 insert_base_points => base_points,
316 insert_amplitude => amplitude,
317 insert_period => period,
318 insert_coefficient => coefficient,
319 insert_points_per_resource => points_per_resource,
320 insert_chain_multiplier => chain_multiplier,
321 insert_effect_radius => radius,
322 }
323
324 effect_insert! {
325 effect f64;
326 insert_power_coefficient_mastery_add => power_coefficient_mastery_add,
327 }
328}