azalea-core 0.16.0+mc26.1

Miscellaneous things in Azalea.
Documentation
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<ValueEffect>,
    },
    Exponential {
        base: LevelBasedValue,
        exponent: LevelBasedValue,
    },
}

impl simdnbt::Deserialize for ValueEffect {
    fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
        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<LevelBasedValue>,
        power: Box<LevelBasedValue>,
    },
    Linear {
        base: f32,
        per_level_above_first: f32,
    },
    LevelsSquared {
        added: f32,
    },
    Clamped {
        value: Box<LevelBasedValue>,
        min: f32,
        max: f32,
    },
    Fraction {
        numerator: Box<LevelBasedValue>,
        denominator: Box<LevelBasedValue>,
    },
    Lookup {
        values: Vec<f32>,
        fallback: Box<LevelBasedValue>,
    },
}
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<Self> {
        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<Self, DeserializeError> {
        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)
    }
}