azalea_core/registry_holder/
entity_effect.rs

1use 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    /// IDs of mob effects.
93    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// TODO: in vanilla this is just a HolderSetCodec using a RegistryFixedCodec,
101// azalea registries should probably be refactored first tho
102#[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    // #[simdnbt(compact)]
170    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            // TODO: this will probably break in the future because it's only handling lists
179            // of strings. you should refactor simdnbt to have an owned NbtTag enum that
180            // contains the borrow types so this works for more than just
181            // strings.
182            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}