1use std::{
2 collections::HashMap,
3 io::{self, Cursor, Write},
4};
5
6use azalea_buf::{AzBuf, BufReadError};
7use azalea_core::{attribute_modifier_operation::AttributeModifierOperation, bitset::FixedBitSet};
8use azalea_inventory::components::AttributeModifier;
9use azalea_registry::{
10 builtin::{Attribute, MobEffect},
11 identifier::Identifier,
12};
13
14#[derive(AzBuf, Clone, Debug, Default, PartialEq)]
16pub struct MobEffectData {
17 #[var]
19 pub amplifier: i32,
20 #[var]
22 pub duration: i32,
23
24 pub flags: MobEffectFlags,
25}
26
27#[derive(Clone, Debug, Default, PartialEq)]
28pub struct MobEffectFlags {
29 pub ambient: bool,
30 pub show_particles: bool,
31 pub show_icon: bool,
32 pub blend: bool,
33}
34
35impl AzBuf for MobEffectFlags {
36 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
37 let bitset = FixedBitSet::<8>::azalea_read(buf)?;
38 let ambient = bitset.index(0);
39 let show_particles = bitset.index(1);
40 let show_icon = bitset.index(2);
41 let blend = bitset.index(3);
42 Ok(Self {
43 ambient,
44 show_particles,
45 show_icon,
46 blend,
47 })
48 }
49 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
50 let mut bitset = FixedBitSet::<8>::new();
51 if self.ambient {
52 bitset.set(0);
53 }
54 if self.show_particles {
55 bitset.set(1);
56 }
57 if self.show_icon {
58 bitset.set(2);
59 }
60 if self.blend {
61 bitset.set(3);
62 }
63 bitset.azalea_write(buf)
64 }
65}
66
67#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
69#[derive(Clone, Debug, Default)]
70pub struct ActiveEffects(pub HashMap<MobEffect, MobEffectData>);
71impl ActiveEffects {
72 pub fn insert(&mut self, effect: MobEffect, data: MobEffectData) -> Option<MobEffectData> {
73 self.0.insert(effect, data)
74 }
75
76 pub fn remove(&mut self, effect: MobEffect) -> Option<MobEffectData> {
77 self.0.remove(&effect)
78 }
79
80 pub fn get_level(&self, effect: MobEffect) -> Option<i32> {
82 self.0.get(&effect).map(|data| data.amplifier)
83 }
84
85 pub fn get(&self, effect: MobEffect) -> Option<&MobEffectData> {
86 self.0.get(&effect)
87 }
88
89 pub fn get_dig_speed_amplifier(&self) -> Option<i32> {
91 let haste_level = self
92 .get_level(MobEffect::Haste)
93 .map(|level| level + 1)
94 .unwrap_or_default();
95 let conduit_power_level = self
96 .get_level(MobEffect::ConduitPower)
97 .map(|level| level + 1)
98 .unwrap_or_default();
99
100 let effect_plus_one = i32::max(haste_level, conduit_power_level);
101 if effect_plus_one > 0 {
102 Some(effect_plus_one - 1)
103 } else {
104 None
105 }
106 }
107}
108
109pub fn attribute_modifier_for_effect(id: MobEffect) -> Option<(Attribute, AttributeTemplate)> {
110 Some(match id {
111 MobEffect::Speed => (
112 Attribute::MovementSpeed,
113 AttributeTemplate::new(
114 "effect.speed",
115 0.2f32 as f64,
116 AttributeModifierOperation::AddMultipliedTotal,
117 ),
118 ),
119 MobEffect::Slowness => (
120 Attribute::MovementSpeed,
121 AttributeTemplate::new(
122 "effect.slowness",
123 -0.15f32 as f64,
124 AttributeModifierOperation::AddMultipliedTotal,
125 ),
126 ),
127 MobEffect::Haste => (
128 Attribute::AttackSpeed,
129 AttributeTemplate::new(
130 "effect.haste",
131 0.1f32 as f64,
132 AttributeModifierOperation::AddMultipliedTotal,
133 ),
134 ),
135 MobEffect::MiningFatigue => (
136 Attribute::AttackSpeed,
137 AttributeTemplate::new(
138 "effect.mining_fatigue",
139 -0.1f32 as f64,
140 AttributeModifierOperation::AddMultipliedTotal,
141 ),
142 ),
143 MobEffect::Strength => (
144 Attribute::AttackDamage,
145 AttributeTemplate::new("effect.strength", 3.0, AttributeModifierOperation::AddValue),
146 ),
147 MobEffect::JumpBoost => (
148 Attribute::SafeFallDistance,
149 AttributeTemplate::new(
150 "effect.jump_boost",
151 1.0,
152 AttributeModifierOperation::AddValue,
153 ),
154 ),
155 MobEffect::Invisibility => (
156 Attribute::WaypointTransmitRange,
157 AttributeTemplate::new(
158 "effect.waypoint_transmit_range_hide",
159 -1.0,
160 AttributeModifierOperation::AddMultipliedTotal,
161 ),
162 ),
163 MobEffect::Weakness => (
164 Attribute::AttackDamage,
165 AttributeTemplate::new(
166 "effect.weakness",
167 -4.0,
168 AttributeModifierOperation::AddValue,
169 ),
170 ),
171 MobEffect::HealthBoost => (
172 Attribute::MaxHealth,
173 AttributeTemplate::new(
174 "effect.health_boost",
175 4.0,
176 AttributeModifierOperation::AddValue,
177 ),
178 ),
179 MobEffect::Absorption => (
180 Attribute::MaxAbsorption,
181 AttributeTemplate::new(
182 "effect.absorption",
183 4.0,
184 AttributeModifierOperation::AddValue,
185 ),
186 ),
187 MobEffect::Luck => (
188 Attribute::Luck,
189 AttributeTemplate::new("effect.luck", 1.0, AttributeModifierOperation::AddValue),
190 ),
191 MobEffect::Unluck => (
192 Attribute::Luck,
193 AttributeTemplate::new("effect.unluck", -1.0, AttributeModifierOperation::AddValue),
194 ),
195 _ => return None,
196 })
197}
198
199pub struct AttributeTemplate(AttributeModifier);
200impl AttributeTemplate {
201 pub fn new(id: &str, amount: f64, operation: AttributeModifierOperation) -> Self {
202 Self(AttributeModifier {
203 id: Identifier::from(id),
204 amount,
205 operation,
206 })
207 }
208 pub fn create(self, amplifier: i32) -> AttributeModifier {
209 AttributeModifier {
210 id: self.0.id,
211 amount: self.0.amount * (amplifier + 1) as f64,
212 operation: self.0.operation,
213 }
214 }
215}