wowlab_engine_adapter_data/
rows.rs1use wowlab_engine_ports::{EnchantmentEffect, EnchantmentRow, ResolverError, SpellId};
2use wowlab_types::data::{EnchantmentFlat, SpellEffect};
3
4const ENCHANTMENT_EFFECT_COUNT: usize = 3;
5
6pub(crate) struct EnchantmentRowData {
7 id: i32,
8 name: String,
9 duration: i32,
10 effect_types: [i32; ENCHANTMENT_EFFECT_COUNT],
11 effect_args: [i32; ENCHANTMENT_EFFECT_COUNT],
12 effect_points_min: [Option<f64>; ENCHANTMENT_EFFECT_COUNT],
13 effect_scaling_points: [Option<f64>; ENCHANTMENT_EFFECT_COUNT],
14 scaling_class: i32,
15 item_level_min: i32,
16 item_level_max: i32,
17 max_level: i32,
18}
19
20impl From<&EnchantmentFlat> for EnchantmentRowData {
21 fn from(flat: &EnchantmentFlat) -> Self {
22 Self {
23 id: flat.id,
24 name: flat.name.clone(),
25 duration: flat.duration,
26 effect_types: [flat.effect_0, flat.effect_1, flat.effect_2],
27 effect_args: [flat.effect_arg_0, flat.effect_arg_1, flat.effect_arg_2],
28 effect_points_min: [
29 Some(f64::from(flat.effect_points_min_0)),
30 Some(f64::from(flat.effect_points_min_1)),
31 Some(f64::from(flat.effect_points_min_2)),
32 ],
33 effect_scaling_points: [
34 Some(f64::from(flat.effect_scaling_points_0)),
35 Some(f64::from(flat.effect_scaling_points_1)),
36 Some(f64::from(flat.effect_scaling_points_2)),
37 ],
38 scaling_class: flat.scaling_class,
39 item_level_min: flat.item_level_min,
40 item_level_max: flat.item_level_max,
41 max_level: flat.max_level,
42 }
43 }
44}
45
46#[derive(Debug, serde::Deserialize)]
47pub(crate) struct EnchantmentRowWire {
48 id: i32,
49 name: String,
50 #[serde(default)]
51 duration: i32,
52 effect_0: i32,
53 effect_1: i32,
54 effect_2: i32,
55 effect_arg_0: i32,
56 effect_arg_1: i32,
57 effect_arg_2: i32,
58 #[serde(default)]
59 effect_points_min_0: Option<f64>,
60 #[serde(default)]
61 effect_points_min_1: Option<f64>,
62 #[serde(default)]
63 effect_points_min_2: Option<f64>,
64 #[serde(default)]
65 effect_scaling_points_0: Option<f64>,
66 #[serde(default)]
67 effect_scaling_points_1: Option<f64>,
68 #[serde(default)]
69 effect_scaling_points_2: Option<f64>,
70 #[serde(default)]
71 scaling_class: i32,
72 #[serde(default)]
73 item_level_min: i32,
74 #[serde(default)]
75 item_level_max: i32,
76 #[serde(default)]
77 max_level: i32,
78}
79
80impl From<EnchantmentRowWire> for EnchantmentRowData {
81 fn from(wire: EnchantmentRowWire) -> Self {
82 if wire.duration < 0 {
83 tracing::warn!(
84 enchantment_id = wire.id,
85 duration_ms = wire.duration,
86 "Clamping negative enchantment duration"
87 );
88 }
89
90 Self {
91 id: wire.id,
92 name: wire.name,
93 duration: wire.duration,
94 effect_types: [wire.effect_0, wire.effect_1, wire.effect_2],
95 effect_args: [wire.effect_arg_0, wire.effect_arg_1, wire.effect_arg_2],
96 effect_points_min: [
97 wire.effect_points_min_0,
98 wire.effect_points_min_1,
99 wire.effect_points_min_2,
100 ],
101 effect_scaling_points: [
102 wire.effect_scaling_points_0,
103 wire.effect_scaling_points_1,
104 wire.effect_scaling_points_2,
105 ],
106 scaling_class: wire.scaling_class,
107 item_level_min: wire.item_level_min,
108 item_level_max: wire.item_level_max,
109 max_level: wire.max_level,
110 }
111 }
112}
113
114pub(crate) fn enchantment_row(source: impl Into<EnchantmentRowData>) -> EnchantmentRow {
115 let source = source.into();
116 let [effect_type_0, effect_type_1, effect_type_2] = source.effect_types;
117 let [effect_arg_0, effect_arg_1, effect_arg_2] = source.effect_args;
118 let [points_min_0, points_min_1, points_min_2] = source.effect_points_min;
119 let [scaling_points_0, scaling_points_1, scaling_points_2] = source.effect_scaling_points;
120 let effect = |effect_type, effect_arg, points_min, scaling_points| EnchantmentEffect {
121 effect_type,
122 effect_arg,
123 points_min,
124 scaling_points,
125 };
126 let effects = [
127 effect(effect_type_0, effect_arg_0, points_min_0, scaling_points_0),
128 effect(effect_type_1, effect_arg_1, points_min_1, scaling_points_1),
129 effect(effect_type_2, effect_arg_2, points_min_2, scaling_points_2),
130 ];
131
132 EnchantmentRow {
133 id: source.id,
134 name: source.name,
135 duration_ms: u32::try_from(source.duration).unwrap_or_default(),
136 effects,
137 scaling_class: source.scaling_class,
138 item_level_min: source.item_level_min,
139 item_level_max: source.item_level_max,
140 max_level: source.max_level,
141 }
142}
143
144pub(crate) fn spell_override_pairs(
145 rows: impl IntoIterator<Item = (i32, i32)>,
146) -> Vec<(SpellId, SpellId)> {
147 rows.into_iter()
148 .filter(|&(overrides_spell_id, _)| overrides_spell_id > 0)
149 .map(|(overrides_spell_id, spell_id)| {
150 (SpellId::new(overrides_spell_id), SpellId::new(spell_id))
151 })
152 .collect()
153}
154
155pub(crate) fn racial_spell_ids(
157 rows: impl IntoIterator<Item = (i32, i32)>,
158 class_id: i32,
159) -> Vec<SpellId> {
160 let class_bit = u32::try_from(class_id - 1)
161 .ok()
162 .and_then(|shift| 1_i32.checked_shl(shift))
163 .unwrap_or_default();
164
165 rows.into_iter()
166 .filter(|&(_, class_mask)| class_mask == 0 || class_mask & class_bit != 0)
167 .map(|(spell_id, _)| SpellId::new(spell_id))
168 .collect()
169}
170
171pub(crate) fn validate_effect_index(
173 spell_id: SpellId,
174 effect_index: u8,
175 effects: &[SpellEffect],
176) -> Result<SpellEffect, ResolverError> {
177 if effect_index == 0 {
178 return Err(ResolverError::spell_effect_not_found(
179 spell_id,
180 effect_index,
181 ));
182 }
183 effects
184 .get((effect_index as usize) - 1)
185 .cloned()
186 .ok_or_else(|| ResolverError::spell_effect_not_found(spell_id, effect_index))
187}
188#[cfg(test)]
191mod tests;