steel_registry/mob_effect/
mod.rs1use std::hash::{Hash, Hasher};
2
3pub mod instance;
4
5use crate::RegistryTags;
6use crate::attribute::{AttributeModifierOperation, AttributeRef};
7use crate::particle_type::{ColorParticleOption, ParticleData, ParticleTypeRef};
8use rustc_hash::FxHashMap;
9use steel_utils::{ArgbColor, Identifier, RgbColor};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum MobEffectCategory {
13 Beneficial,
14 Harmful,
15 Neutral,
16}
17
18#[derive(Debug)]
19pub struct MobEffectAttributeModifier {
20 pub attribute: AttributeRef,
21 pub id: Identifier,
22 pub amount: f64,
23 pub operation: AttributeModifierOperation,
24}
25
26#[derive(Debug, Clone, Copy)]
28pub enum MobEffectParticle {
29 EffectColor {
31 particle_type: ParticleTypeRef,
32 regular_alpha: u8,
33 ambient_alpha: u8,
34 },
35 Simple(ParticleTypeRef),
37 FixedColor {
39 particle_type: ParticleTypeRef,
40 color: ArgbColor,
41 },
42}
43
44impl MobEffectParticle {
45 #[must_use]
46 pub fn create(self, effect_color: RgbColor, ambient: bool) -> ParticleData {
47 match self {
48 Self::EffectColor {
49 particle_type,
50 regular_alpha,
51 ambient_alpha,
52 } => {
53 let alpha = if ambient {
54 ambient_alpha
55 } else {
56 regular_alpha
57 };
58 ParticleData::new(
59 particle_type,
60 ColorParticleOption::new(effect_color.with_alpha(alpha)),
61 )
62 }
63 Self::Simple(particle_type) => ParticleData::simple(particle_type),
64 Self::FixedColor {
65 particle_type,
66 color,
67 } => ParticleData::new(particle_type, ColorParticleOption::new(color)),
68 }
69 }
70}
71
72#[derive(Debug)]
73pub struct MobEffect {
74 pub key: Identifier,
75 pub category: MobEffectCategory,
76 pub color: RgbColor,
77 pub particle: MobEffectParticle,
78 pub attribute_modifiers: &'static [MobEffectAttributeModifier],
79}
80
81impl MobEffect {
82 #[must_use]
84 pub fn create_particle_options(&self, ambient: bool) -> ParticleData {
85 self.particle.create(self.color, ambient)
86 }
87
88 #[must_use]
90 pub fn packet_holder_id(&self) -> i32 {
91 let id = crate::RegistryEntry::id(self);
92 debug_assert!(i32::try_from(id).is_ok());
93 id as i32
94 }
95}
96
97impl Hash for MobEffect {
98 fn hash<H: Hasher>(&self, state: &mut H) {
99 self.key.hash(state);
100 }
101}
102
103pub type MobEffectRef = &'static MobEffect;
104
105pub struct MobEffectRegistry {
106 effects_by_id: Vec<MobEffectRef>,
107 effects_by_key: FxHashMap<Identifier, usize>,
108 tags: RegistryTags,
109 allows_registering: bool,
110}
111
112impl Default for MobEffectRegistry {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl MobEffectRegistry {
119 #[must_use]
120 pub fn new() -> Self {
121 Self {
122 effects_by_id: Vec::new(),
123 effects_by_key: FxHashMap::default(),
124 tags: RegistryTags::default(),
125 allows_registering: true,
126 }
127 }
128
129 pub fn register(&mut self, effect: MobEffectRef) {
130 assert!(
131 self.allows_registering,
132 "Cannot register mob effects after the registry has been frozen"
133 );
134 let idx = self.effects_by_id.len();
135 self.effects_by_key.insert(effect.key.clone(), idx);
136 self.effects_by_id.push(effect);
137 }
138
139 pub fn iter(&self) -> impl Iterator<Item = (usize, MobEffectRef)> + '_ {
140 self.effects_by_id
141 .iter()
142 .enumerate()
143 .map(|(id, &effect)| (id, effect))
144 }
145}
146
147crate::impl_registry!(
148 MobEffectRegistry,
149 MobEffect,
150 effects_by_id,
151 effects_by_key,
152 mob_effects
153);
154
155#[cfg(test)]
156mod tests {
157 use crate::particle_type::{ColorParticleOption, SimpleParticleOptions};
158 use crate::{vanilla_mob_effects, vanilla_particle_types};
159
160 #[test]
161 fn generated_effect_particles_match_vanilla_factories() {
162 let speed = vanilla_mob_effects::SPEED;
163 let regular = speed.create_particle_options(false);
164 let ambient = speed.create_particle_options(true);
165 let Some(regular_color) = regular.downcast_ref::<ColorParticleOption>() else {
166 panic!("speed should use color particle options");
167 };
168 let Some(ambient_color) = ambient.downcast_ref::<ColorParticleOption>() else {
169 panic!("ambient speed should use color particle options");
170 };
171
172 assert_eq!(
173 regular.particle_type().key,
174 vanilla_particle_types::ENTITY_EFFECT.key
175 );
176 assert_eq!(regular_color.color().alpha(), 255);
177 assert_eq!(ambient_color.color().alpha(), 38);
178 assert_eq!(
179 regular_color.color().rgb().raw() & 0x00ff_ffff,
180 speed.color.raw()
181 );
182
183 let trial_omen = vanilla_mob_effects::TRIAL_OMEN;
184 let trial_particle = trial_omen.create_particle_options(false);
185 assert_eq!(
186 trial_particle.particle_type().key,
187 vanilla_particle_types::TRIAL_OMEN.key
188 );
189 assert!(
190 trial_particle
191 .downcast_ref::<SimpleParticleOptions>()
192 .is_some()
193 );
194 }
195}
196
197crate::impl_tagged_registry!(MobEffectRegistry, effects_by_key, "mob effect");