azalea_core/registry_holder/
value.rs

1use azalea_registry::{
2    builtin::{
3        Attribute, EnchantmentLevelBasedValueKind as LevelBasedValueKind,
4        EnchantmentValueEffectKind as ValueEffectKind,
5    },
6    identifier::Identifier,
7};
8use simdnbt::{
9    DeserializeError, FromNbtTag,
10    borrow::{NbtCompound, NbtTag},
11};
12
13use crate::{
14    attribute_modifier_operation::AttributeModifierOperation,
15    registry_holder::{components::impl_from_effect_nbt_tag, get_in_compound},
16};
17
18#[derive(Clone, Debug)]
19pub enum ValueEffect {
20    Set {
21        value: LevelBasedValue,
22    },
23    Add {
24        value: LevelBasedValue,
25    },
26    Multiply {
27        factor: LevelBasedValue,
28    },
29    RemoveBinomial {
30        chance: LevelBasedValue,
31    },
32    AllOf {
33        effects: Vec<ValueEffect>,
34    },
35    Exponential {
36        base: LevelBasedValue,
37        exponent: LevelBasedValue,
38    },
39}
40
41impl simdnbt::Deserialize for ValueEffect {
42    fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
43        let kind = get_in_compound(&nbt, "type")?;
44        let value = match kind {
45            ValueEffectKind::Set => {
46                let value = get_in_compound(&nbt, "value")?;
47                Self::Set { value }
48            }
49            ValueEffectKind::Add => {
50                let value = get_in_compound(&nbt, "value")?;
51                Self::Add { value }
52            }
53            ValueEffectKind::Multiply => {
54                let factor = get_in_compound(&nbt, "factor")?;
55                Self::Multiply { factor }
56            }
57            ValueEffectKind::RemoveBinomial => {
58                let chance = get_in_compound(&nbt, "chance")?;
59                Self::RemoveBinomial { chance }
60            }
61            ValueEffectKind::AllOf => {
62                let effects = get_in_compound(&nbt, "effects")?;
63                Self::AllOf { effects }
64            }
65            ValueEffectKind::Exponential => {
66                let base = get_in_compound(&nbt, "base")?;
67                let exponent = get_in_compound(&nbt, "exponent")?;
68                Self::Exponential { base, exponent }
69            }
70        };
71        Ok(value)
72    }
73}
74impl_from_effect_nbt_tag!(ValueEffect);
75
76#[derive(Clone, Debug, simdnbt::Deserialize)]
77pub struct AttributeEffect {
78    pub id: Identifier,
79    pub attribute: Attribute,
80    pub amount: LevelBasedValue,
81    pub operation: AttributeModifierOperation,
82}
83impl_from_effect_nbt_tag!(AttributeEffect);
84
85#[derive(Clone, Debug)]
86pub enum LevelBasedValue {
87    Constant(f32),
88    Exponent {
89        base: Box<LevelBasedValue>,
90        power: Box<LevelBasedValue>,
91    },
92    Linear {
93        base: f32,
94        per_level_above_first: f32,
95    },
96    LevelsSquared {
97        added: f32,
98    },
99    Clamped {
100        value: Box<LevelBasedValue>,
101        min: f32,
102        max: f32,
103    },
104    Fraction {
105        numerator: Box<LevelBasedValue>,
106        denominator: Box<LevelBasedValue>,
107    },
108    Lookup {
109        values: Vec<f32>,
110        fallback: Box<LevelBasedValue>,
111    },
112}
113impl LevelBasedValue {
114    pub fn calculate(&self, n: i32) -> f32 {
115        match self {
116            LevelBasedValue::Constant(v) => *v,
117            LevelBasedValue::Exponent { base, power } => {
118                (base.calculate(n) as f64).powf(power.calculate(n) as f64) as f32
119            }
120            LevelBasedValue::Linear {
121                base,
122                per_level_above_first,
123            } => *base + *per_level_above_first * ((n - 1) as f32),
124            LevelBasedValue::LevelsSquared { added } => (n * n) as f32 + *added,
125            LevelBasedValue::Clamped { value, min, max } => value.calculate(n).clamp(*min, *max),
126            LevelBasedValue::Fraction {
127                numerator,
128                denominator,
129            } => {
130                let value = denominator.calculate(n);
131                if value == 0. {
132                    0.
133                } else {
134                    numerator.calculate(n) / value
135                }
136            }
137            LevelBasedValue::Lookup { values, fallback } => values
138                .get((n - 1) as usize)
139                .copied()
140                .unwrap_or_else(|| fallback.calculate(n)),
141        }
142    }
143}
144
145impl Default for LevelBasedValue {
146    fn default() -> Self {
147        Self::Constant(0.)
148    }
149}
150
151impl FromNbtTag for LevelBasedValue {
152    fn from_nbt_tag(tag: NbtTag) -> Option<Self> {
153        if let Some(f) = tag.float() {
154            return Some(Self::Constant(f));
155        }
156        if let Some(c) = tag.compound() {
157            return Self::from_compound(c).ok();
158        }
159        None
160    }
161}
162impl LevelBasedValue {
163    fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
164        let kind = get_in_compound(&nbt, "type")?;
165        let value = match kind {
166            LevelBasedValueKind::Exponent => {
167                let base = Box::new(get_in_compound(&nbt, "base")?);
168                let power = Box::new(get_in_compound(&nbt, "power")?);
169                return Ok(Self::Exponent { base, power });
170            }
171            LevelBasedValueKind::Linear => {
172                let base = get_in_compound(&nbt, "base")?;
173                let per_level_above_first = get_in_compound(&nbt, "per_level_above_first")?;
174                Self::Linear {
175                    base,
176                    per_level_above_first,
177                }
178            }
179            LevelBasedValueKind::LevelsSquared => {
180                let added = get_in_compound(&nbt, "added")?;
181                Self::LevelsSquared { added }
182            }
183            LevelBasedValueKind::Clamped => {
184                let value = Box::new(get_in_compound(&nbt, "value")?);
185                let min = get_in_compound(&nbt, "min")?;
186                let max = get_in_compound(&nbt, "max")?;
187                Self::Clamped { value, min, max }
188            }
189            LevelBasedValueKind::Fraction => {
190                let numerator = Box::new(get_in_compound(&nbt, "numerator")?);
191                let denominator = Box::new(get_in_compound(&nbt, "denominator")?);
192                Self::Fraction {
193                    numerator,
194                    denominator,
195                }
196            }
197            LevelBasedValueKind::Lookup => {
198                let values = nbt
199                    .list("values")
200                    .ok_or(DeserializeError::MissingField)?
201                    .floats()
202                    .ok_or(DeserializeError::MissingField)?;
203                let fallback = Box::new(get_in_compound(&nbt, "fallback")?);
204                Self::Lookup { values, fallback }
205            }
206        };
207        Ok(value)
208    }
209}