use azalea_registry::{ builtin::{ Attribute, EnchantmentLevelBasedValueKind as LevelBasedValueKind, EnchantmentValueEffectKind as ValueEffectKind, }, identifier::Identifier, }; use simdnbt::{ DeserializeError, FromNbtTag, borrow::{NbtCompound, NbtTag}, }; use crate::{ attribute_modifier_operation::AttributeModifierOperation, registry_holder::{components::impl_from_effect_nbt_tag, get_in_compound}, }; #[derive(Clone, Debug)] pub enum ValueEffect { Set { value: LevelBasedValue, }, Add { value: LevelBasedValue, }, Multiply { factor: LevelBasedValue, }, RemoveBinomial { chance: LevelBasedValue, }, AllOf { effects: Vec, }, Exponential { base: LevelBasedValue, exponent: LevelBasedValue, }, } impl simdnbt::Deserialize for ValueEffect { fn from_compound(nbt: NbtCompound) -> Result { let kind = get_in_compound(&nbt, "type")?; let value = match kind { ValueEffectKind::Set => { let value = get_in_compound(&nbt, "value")?; Self::Set { value } } ValueEffectKind::Add => { let value = get_in_compound(&nbt, "value")?; Self::Add { value } } ValueEffectKind::Multiply => { let factor = get_in_compound(&nbt, "factor")?; Self::Multiply { factor } } ValueEffectKind::RemoveBinomial => { let chance = get_in_compound(&nbt, "chance")?; Self::RemoveBinomial { chance } } ValueEffectKind::AllOf => { let effects = get_in_compound(&nbt, "effects")?; Self::AllOf { effects } } ValueEffectKind::Exponential => { let base = get_in_compound(&nbt, "base")?; let exponent = get_in_compound(&nbt, "exponent")?; Self::Exponential { base, exponent } } }; Ok(value) } } impl_from_effect_nbt_tag!(ValueEffect); #[derive(Clone, Debug, simdnbt::Deserialize)] pub struct AttributeEffect { pub id: Identifier, pub attribute: Attribute, pub amount: LevelBasedValue, pub operation: AttributeModifierOperation, } impl_from_effect_nbt_tag!(AttributeEffect); #[derive(Clone, Debug)] pub enum LevelBasedValue { Constant(f32), Exponent { base: Box, power: Box, }, Linear { base: f32, per_level_above_first: f32, }, LevelsSquared { added: f32, }, Clamped { value: Box, min: f32, max: f32, }, Fraction { numerator: Box, denominator: Box, }, Lookup { values: Vec, fallback: Box, }, } impl LevelBasedValue { pub fn calculate(&self, n: i32) -> f32 { match self { LevelBasedValue::Constant(v) => *v, LevelBasedValue::Exponent { base, power } => { (base.calculate(n) as f64).powf(power.calculate(n) as f64) as f32 } LevelBasedValue::Linear { base, per_level_above_first, } => *base + *per_level_above_first * ((n - 1) as f32), LevelBasedValue::LevelsSquared { added } => (n * n) as f32 + *added, LevelBasedValue::Clamped { value, min, max } => value.calculate(n).clamp(*min, *max), LevelBasedValue::Fraction { numerator, denominator, } => { let value = denominator.calculate(n); if value == 0. { 0. } else { numerator.calculate(n) / value } } LevelBasedValue::Lookup { values, fallback } => values .get((n - 1) as usize) .copied() .unwrap_or_else(|| fallback.calculate(n)), } } } impl Default for LevelBasedValue { fn default() -> Self { Self::Constant(0.) } } impl FromNbtTag for LevelBasedValue { fn from_nbt_tag(tag: NbtTag) -> Option { if let Some(f) = tag.float() { return Some(Self::Constant(f)); } if let Some(c) = tag.compound() { return Self::from_compound(c).ok(); } None } } impl LevelBasedValue { fn from_compound(nbt: NbtCompound) -> Result { let kind = get_in_compound(&nbt, "type")?; let value = match kind { LevelBasedValueKind::Exponent => { let base = Box::new(get_in_compound(&nbt, "base")?); let power = Box::new(get_in_compound(&nbt, "power")?); return Ok(Self::Exponent { base, power }); } LevelBasedValueKind::Linear => { let base = get_in_compound(&nbt, "base")?; let per_level_above_first = get_in_compound(&nbt, "per_level_above_first")?; Self::Linear { base, per_level_above_first, } } LevelBasedValueKind::LevelsSquared => { let added = get_in_compound(&nbt, "added")?; Self::LevelsSquared { added } } LevelBasedValueKind::Clamped => { let value = Box::new(get_in_compound(&nbt, "value")?); let min = get_in_compound(&nbt, "min")?; let max = get_in_compound(&nbt, "max")?; Self::Clamped { value, min, max } } LevelBasedValueKind::Fraction => { let numerator = Box::new(get_in_compound(&nbt, "numerator")?); let denominator = Box::new(get_in_compound(&nbt, "denominator")?); Self::Fraction { numerator, denominator, } } LevelBasedValueKind::Lookup => { let values = nbt .list("values") .ok_or(DeserializeError::MissingField)? .floats() .ok_or(DeserializeError::MissingField)?; let fallback = Box::new(get_in_compound(&nbt, "fallback")?); Self::Lookup { values, fallback } } }; Ok(value) } }