azalea_core/registry_holder/
entity_effect.rs1use std::{collections::HashMap, str::FromStr};
2
3use azalea_registry::{
4 Holder,
5 builtin::{
6 EnchantmentEntityEffectKind as EntityEffectKind, GameEvent, ParticleKind, SoundEvent,
7 },
8 data::DamageKindKey,
9 identifier::Identifier,
10};
11use simdnbt::{
12 Deserialize, DeserializeError,
13 borrow::{NbtCompound, NbtTag},
14};
15use tracing::error;
16
17use crate::{
18 position::{Vec3, Vec3i},
19 registry_holder::{
20 block_predicate::BlockPredicate, block_state_provider::BlockStateProvider,
21 float_provider::FloatProvider, get_in_compound, value::LevelBasedValue,
22 },
23 sound::CustomSound,
24};
25
26#[derive(Clone, Debug)]
27pub enum EntityEffect {
28 AllOf(AllOf),
29 ApplyMobEffect(ApplyMobEffect),
30 ChangeItemDamage(ChangeItemDamage),
31 DamageEntity(DamageEntity),
32 Explode(Explode),
33 Ignite(Ignite),
34 ApplyImpulse(ApplyEntityImpulse),
35 ApplyExhaustion(ApplyExhaustion),
36 PlaySound(PlaySound),
37 ReplaceBlock(ReplaceBlock),
38 ReplaceDisk(ReplaceDisk),
39 RunFunction(RunFunction),
40 SetBlockProperties(SetBlockProperties),
41 SpawnParticles(SpawnParticles),
42 SummonEntity(SummonEntity),
43}
44
45impl Deserialize for EntityEffect {
46 fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
47 let kind = get_in_compound(&nbt, "type")?;
48 let res = match kind {
49 EntityEffectKind::AllOf => Deserialize::from_compound(nbt).map(Self::AllOf),
50 EntityEffectKind::ApplyMobEffect => {
51 Deserialize::from_compound(nbt).map(Self::ApplyMobEffect)
52 }
53 EntityEffectKind::ChangeItemDamage => {
54 Deserialize::from_compound(nbt).map(Self::ChangeItemDamage)
55 }
56 EntityEffectKind::DamageEntity => {
57 Deserialize::from_compound(nbt).map(Self::DamageEntity)
58 }
59 EntityEffectKind::Explode => Deserialize::from_compound(nbt).map(Self::Explode),
60 EntityEffectKind::Ignite => Deserialize::from_compound(nbt).map(Self::Ignite),
61 EntityEffectKind::ApplyImpulse => {
62 Deserialize::from_compound(nbt).map(Self::ApplyImpulse)
63 }
64 EntityEffectKind::ApplyExhaustion => {
65 Deserialize::from_compound(nbt).map(Self::ApplyExhaustion)
66 }
67 EntityEffectKind::PlaySound => Deserialize::from_compound(nbt).map(Self::PlaySound),
68 EntityEffectKind::ReplaceBlock => {
69 Deserialize::from_compound(nbt).map(Self::ReplaceBlock)
70 }
71 EntityEffectKind::ReplaceDisk => Deserialize::from_compound(nbt).map(Self::ReplaceDisk),
72 EntityEffectKind::RunFunction => Deserialize::from_compound(nbt).map(Self::RunFunction),
73 EntityEffectKind::SetBlockProperties => {
74 Deserialize::from_compound(nbt).map(Self::SetBlockProperties)
75 }
76 EntityEffectKind::SpawnParticles => {
77 Deserialize::from_compound(nbt).map(Self::SpawnParticles)
78 }
79 EntityEffectKind::SummonEntity => {
80 Deserialize::from_compound(nbt).map(Self::SummonEntity)
81 }
82 };
83 if res.is_err() {
84 error!("Error deserializing EntityEffect {kind}: {nbt:?}");
85 }
86 res
87 }
88}
89
90#[derive(Clone, Debug, simdnbt::Deserialize)]
91pub struct AllOf {
92 pub effects: Vec<EntityEffect>,
93}
94
95#[derive(Clone, Debug, simdnbt::Deserialize)]
96pub struct ApplyMobEffect {
97 pub to_apply: HomogeneousList,
99 pub min_duration: LevelBasedValue,
100 pub max_duration: LevelBasedValue,
101 pub min_amplifier: LevelBasedValue,
102 pub max_amplifier: LevelBasedValue,
103}
104
105#[derive(Clone, Debug, Default)]
108pub struct HomogeneousList {
109 pub ids: Vec<Identifier>,
110}
111impl simdnbt::FromNbtTag for HomogeneousList {
112 fn from_nbt_tag(tag: NbtTag) -> Option<Self> {
113 if let Some(string) = tag.string() {
114 return Some(Self {
115 ids: vec![Identifier::new(string.to_str())],
116 });
117 }
118 if let Some(list) = tag.list()
119 && let Some(strings) = list.strings()
120 {
121 return Some(Self {
122 ids: strings
123 .iter()
124 .map(|&s| Identifier::new(s.to_str()))
125 .collect(),
126 });
127 }
128 None
129 }
130}
131
132#[derive(Clone, Debug, simdnbt::Deserialize)]
133pub struct ChangeItemDamage {
134 pub amount: LevelBasedValue,
135}
136
137#[derive(Clone, Debug, simdnbt::Deserialize)]
138pub struct DamageEntity {
139 pub min_damage: LevelBasedValue,
140 pub max_damage: LevelBasedValue,
141 #[simdnbt(rename = "damage_type")]
142 pub damage_kind: DamageKindKey,
143}
144
145#[derive(Clone, Debug, simdnbt::Deserialize)]
146pub struct Explode {
147 pub attribute_to_user: Option<bool>,
148 #[simdnbt(rename = "damage_type")]
149 pub damage_kind: Option<DamageKindKey>,
150 pub knockback_multiplier: Option<LevelBasedValue>,
151 pub immune_blocks: Option<HomogeneousList>,
152 pub offset: Option<Vec3>,
153}
154
155#[derive(Clone, Debug, simdnbt::Deserialize)]
156pub struct Ignite {
157 pub duration: LevelBasedValue,
158}
159
160#[derive(Clone, Debug, simdnbt::Deserialize)]
161pub struct ApplyEntityImpulse {
162 pub direction: Vec3,
163 pub coordinate_scale: Vec3,
164 pub magnitude: LevelBasedValue,
165}
166
167#[derive(Clone, Debug, simdnbt::Deserialize)]
168pub struct ApplyExhaustion {
169 pub amount: LevelBasedValue,
170}
171
172#[derive(Clone, Debug)]
173pub struct PlaySound {
174 pub sound: Vec<Holder<SoundEvent, CustomSound>>,
176 pub volume: FloatProvider,
177 pub pitch: FloatProvider,
178}
179
180impl Deserialize for PlaySound {
181 fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
182 let sound = if let Some(list) = nbt.list("sound") {
183 list.strings()
188 .ok_or(DeserializeError::MissingField)?
189 .iter()
190 .map(|s| {
191 SoundEvent::from_str(&s.to_str())
192 .map(Holder::Reference)
193 .ok()
194 })
195 .collect::<Option<_>>()
196 .ok_or(DeserializeError::MissingField)?
197 } else {
198 vec![get_in_compound(&nbt, "sound")?]
199 };
200
201 let volume = get_in_compound(&nbt, "volume")?;
202 let pitch = get_in_compound(&nbt, "pitch")?;
203
204 Ok(Self {
205 sound,
206 volume,
207 pitch,
208 })
209 }
210}
211
212#[derive(Clone, Debug, simdnbt::Deserialize)]
213pub struct ReplaceBlock {
214 pub offset: Option<Vec3i>,
215 pub predicate: Option<BlockPredicate>,
216 pub block_state: BlockStateProvider,
217 pub trigger_game_event: Option<GameEvent>,
218}
219
220#[derive(Clone, Debug, simdnbt::Deserialize)]
221pub struct ReplaceDisk {
222 pub radius: LevelBasedValue,
223 pub height: LevelBasedValue,
224 pub offset: Option<Vec3i>,
225 pub predicate: Option<BlockPredicate>,
226 pub block_state: BlockStateProvider,
227 pub trigger_game_event: Option<GameEvent>,
228}
229
230#[derive(Clone, Debug, simdnbt::Deserialize)]
231pub struct RunFunction {
232 pub function: Identifier,
233}
234
235#[derive(Clone, Debug, simdnbt::Deserialize)]
236pub struct SetBlockProperties {
237 pub properties: HashMap<String, String>,
238 pub offset: Option<Vec3i>,
239 pub trigger_game_event: Option<GameEvent>,
240}
241
242#[derive(Clone, Debug, simdnbt::Deserialize)]
243pub struct SpawnParticles {
244 pub particle: ParticleKindCodec,
245 pub horizontal_position: SpawnParticlesPosition,
246 pub vertical_position: SpawnParticlesPosition,
247 pub horizontal_velocity: SpawnParticlesVelocity,
248 pub vertical_velocity: SpawnParticlesVelocity,
249 pub speed: Option<FloatProvider>,
250}
251
252#[derive(Clone, Debug, simdnbt::Deserialize)]
253pub struct ParticleKindCodec {
254 #[simdnbt(rename = "type")]
255 pub kind: ParticleKind,
256}
257
258#[derive(Clone, Debug, simdnbt::Deserialize)]
259pub struct SpawnParticlesPosition {
260 #[simdnbt(rename = "type")]
261 pub kind: Identifier,
262 pub offset: Option<f32>,
263 pub scale: Option<f32>,
264}
265
266#[derive(Clone, Debug, simdnbt::Deserialize)]
267pub struct SpawnParticlesVelocity {
268 pub movement_scale: Option<f32>,
269 pub base: Option<FloatProvider>,
270}
271
272#[derive(Clone, Debug, simdnbt::Deserialize)]
273pub struct SummonEntity {
274 pub entity: HomogeneousList,
275 pub join_team: Option<bool>,
276}