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