use std::{collections::HashMap, str::FromStr};
use azalea_registry::{
Holder,
builtin::{
EnchantmentEntityEffectKind as EntityEffectKind, GameEvent, ParticleKind, SoundEvent,
},
data::DamageKindKey,
identifier::Identifier,
};
use simdnbt::{
Deserialize, DeserializeError,
borrow::{NbtCompound, NbtTag},
};
use tracing::error;
use crate::{
position::{Vec3, Vec3i},
registry_holder::{
block_predicate::BlockPredicate, block_state_provider::BlockStateProvider,
float_provider::FloatProvider, get_in_compound, value::LevelBasedValue,
},
sound::CustomSound,
};
#[derive(Clone, Debug)]
pub enum EntityEffect {
AllOf(AllOf),
ApplyMobEffect(ApplyMobEffect),
ChangeItemDamage(ChangeItemDamage),
DamageEntity(DamageEntity),
Explode(Explode),
Ignite(Ignite),
ApplyImpulse(ApplyEntityImpulse),
ApplyExhaustion(ApplyExhaustion),
PlaySound(PlaySound),
ReplaceBlock(ReplaceBlock),
ReplaceDisk(ReplaceDisk),
RunFunction(RunFunction),
SetBlockProperties(SetBlockProperties),
SpawnParticles(SpawnParticles),
SummonEntity(SummonEntity),
}
impl Deserialize for EntityEffect {
fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
let kind = get_in_compound(&nbt, "type")?;
let res = match kind {
EntityEffectKind::AllOf => Deserialize::from_compound(nbt).map(Self::AllOf),
EntityEffectKind::ApplyMobEffect => {
Deserialize::from_compound(nbt).map(Self::ApplyMobEffect)
}
EntityEffectKind::ChangeItemDamage => {
Deserialize::from_compound(nbt).map(Self::ChangeItemDamage)
}
EntityEffectKind::DamageEntity => {
Deserialize::from_compound(nbt).map(Self::DamageEntity)
}
EntityEffectKind::Explode => Deserialize::from_compound(nbt).map(Self::Explode),
EntityEffectKind::Ignite => Deserialize::from_compound(nbt).map(Self::Ignite),
EntityEffectKind::ApplyImpulse => {
Deserialize::from_compound(nbt).map(Self::ApplyImpulse)
}
EntityEffectKind::ApplyExhaustion => {
Deserialize::from_compound(nbt).map(Self::ApplyExhaustion)
}
EntityEffectKind::PlaySound => Deserialize::from_compound(nbt).map(Self::PlaySound),
EntityEffectKind::ReplaceBlock => {
Deserialize::from_compound(nbt).map(Self::ReplaceBlock)
}
EntityEffectKind::ReplaceDisk => Deserialize::from_compound(nbt).map(Self::ReplaceDisk),
EntityEffectKind::RunFunction => Deserialize::from_compound(nbt).map(Self::RunFunction),
EntityEffectKind::SetBlockProperties => {
Deserialize::from_compound(nbt).map(Self::SetBlockProperties)
}
EntityEffectKind::SpawnParticles => {
Deserialize::from_compound(nbt).map(Self::SpawnParticles)
}
EntityEffectKind::SummonEntity => {
Deserialize::from_compound(nbt).map(Self::SummonEntity)
}
};
if res.is_err() {
error!("Error deserializing EntityEffect {kind}: {nbt:?}");
}
res
}
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct AllOf {
pub effects: Vec<EntityEffect>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ApplyMobEffect {
pub to_apply: HomogeneousList,
pub min_duration: LevelBasedValue,
pub max_duration: LevelBasedValue,
pub min_amplifier: LevelBasedValue,
pub max_amplifier: LevelBasedValue,
}
#[derive(Clone, Debug, Default)]
pub struct HomogeneousList {
pub ids: Vec<Identifier>,
}
impl simdnbt::FromNbtTag for HomogeneousList {
fn from_nbt_tag(tag: NbtTag) -> Option<Self> {
if let Some(string) = tag.string() {
return Some(Self {
ids: vec![Identifier::new(string.to_str())],
});
}
if let Some(list) = tag.list()
&& let Some(strings) = list.strings()
{
return Some(Self {
ids: strings
.iter()
.map(|&s| Identifier::new(s.to_str()))
.collect(),
});
}
None
}
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ChangeItemDamage {
pub amount: LevelBasedValue,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct DamageEntity {
pub min_damage: LevelBasedValue,
pub max_damage: LevelBasedValue,
#[simdnbt(rename = "damage_type")]
pub damage_kind: DamageKindKey,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct Explode {
pub attribute_to_user: Option<bool>,
#[simdnbt(rename = "damage_type")]
pub damage_kind: Option<DamageKindKey>,
pub knockback_multiplier: Option<LevelBasedValue>,
pub immune_blocks: Option<HomogeneousList>,
pub offset: Option<Vec3>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct Ignite {
pub duration: LevelBasedValue,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ApplyEntityImpulse {
pub direction: Vec3,
pub coordinate_scale: Vec3,
pub magnitude: LevelBasedValue,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ApplyExhaustion {
pub amount: LevelBasedValue,
}
#[derive(Clone, Debug)]
pub struct PlaySound {
pub sound: Vec<Holder<SoundEvent, CustomSound>>,
pub volume: FloatProvider,
pub pitch: FloatProvider,
}
impl Deserialize for PlaySound {
fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
let sound = if let Some(list) = nbt.list("sound") {
list.strings()
.ok_or(DeserializeError::MissingField)?
.iter()
.map(|s| {
SoundEvent::from_str(&s.to_str())
.map(Holder::Reference)
.ok()
})
.collect::<Option<_>>()
.ok_or(DeserializeError::MissingField)?
} else {
vec![get_in_compound(&nbt, "sound")?]
};
let volume = get_in_compound(&nbt, "volume")?;
let pitch = get_in_compound(&nbt, "pitch")?;
Ok(Self {
sound,
volume,
pitch,
})
}
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ReplaceBlock {
pub offset: Option<Vec3i>,
pub predicate: Option<BlockPredicate>,
pub block_state: BlockStateProvider,
pub trigger_game_event: Option<GameEvent>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ReplaceDisk {
pub radius: LevelBasedValue,
pub height: LevelBasedValue,
pub offset: Option<Vec3i>,
pub predicate: Option<BlockPredicate>,
pub block_state: BlockStateProvider,
pub trigger_game_event: Option<GameEvent>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct RunFunction {
pub function: Identifier,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct SetBlockProperties {
pub properties: HashMap<String, String>,
pub offset: Option<Vec3i>,
pub trigger_game_event: Option<GameEvent>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct SpawnParticles {
pub particle: ParticleKindCodec,
pub horizontal_position: SpawnParticlesPosition,
pub vertical_position: SpawnParticlesPosition,
pub horizontal_velocity: SpawnParticlesVelocity,
pub vertical_velocity: SpawnParticlesVelocity,
pub speed: Option<FloatProvider>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct ParticleKindCodec {
#[simdnbt(rename = "type")]
pub kind: ParticleKind,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct SpawnParticlesPosition {
#[simdnbt(rename = "type")]
pub kind: Identifier,
pub offset: Option<f32>,
pub scale: Option<f32>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct SpawnParticlesVelocity {
pub movement_scale: Option<f32>,
pub base: Option<FloatProvider>,
}
#[derive(Clone, Debug, simdnbt::Deserialize)]
pub struct SummonEntity {
pub entity: HomogeneousList,
pub join_team: Option<bool>,
}