Skip to main content

azalea_entity/
attributes.rs

1//! Attributes and modifiers for entities.
2//!
3//! Also see <https://minecraft.wiki/w/Attribute>.
4
5use std::collections::{HashMap, hash_map};
6
7use azalea_core::attribute_modifier_operation::AttributeModifierOperation;
8use azalea_inventory::components::AttributeModifier;
9use azalea_registry::{builtin::Attribute, identifier::Identifier};
10use thiserror::Error;
11
12/// The current attribute values for an entity.
13///
14/// Each attribute can have multiple modifiers, and these modifiers are the
15/// result of things like sprinting or enchantments.
16#[cfg_attr(feature = "bevy_ecs", derive(bevy_ecs::component::Component))]
17#[derive(Clone, Debug)]
18pub struct Attributes {
19    pub movement_speed: AttributeInstance,
20    pub sneaking_speed: AttributeInstance,
21    pub attack_speed: AttributeInstance,
22    pub water_movement_efficiency: AttributeInstance,
23    pub mining_efficiency: AttributeInstance,
24    pub block_break_speed: AttributeInstance,
25
26    pub block_interaction_range: AttributeInstance,
27    pub entity_interaction_range: AttributeInstance,
28
29    pub step_height: AttributeInstance,
30}
31
32impl Attributes {
33    /// Returns a mutable reference to the [`AttributeInstance`] for the given
34    /// attribute, or `None` if the attribute isn't implemented.
35    pub fn get_mut(&mut self, attribute: Attribute) -> Option<&mut AttributeInstance> {
36        let value = match attribute {
37            Attribute::MovementSpeed => &mut self.movement_speed,
38            Attribute::SneakingSpeed => &mut self.sneaking_speed,
39            Attribute::AttackSpeed => &mut self.attack_speed,
40            Attribute::WaterMovementEfficiency => &mut self.water_movement_efficiency,
41            Attribute::MiningEfficiency => &mut self.mining_efficiency,
42            Attribute::BlockInteractionRange => &mut self.block_interaction_range,
43            Attribute::EntityInteractionRange => &mut self.entity_interaction_range,
44            Attribute::StepHeight => &mut self.step_height,
45            Attribute::BlockBreakSpeed => &mut self.block_break_speed,
46            _ => return None,
47        };
48        Some(value)
49    }
50}
51
52/// An individual attribute for an entity, which may have any number of
53/// modifiers attached to it.
54#[derive(Clone, Debug)]
55pub struct AttributeInstance {
56    pub base: f64,
57    modifiers_by_id: HashMap<Identifier, AttributeModifier>,
58    // TODO: add cache
59}
60
61/// An error for when we try to call [`AttributeInstance::try_insert`] when the
62/// modifier is already present.
63#[derive(Clone, Debug, Error)]
64#[error("A modifier with this ID is already present.")]
65pub struct AlreadyPresentError;
66
67impl AttributeInstance {
68    pub fn new(base: f64) -> Self {
69        Self {
70            base,
71            modifiers_by_id: HashMap::new(),
72        }
73    }
74
75    pub fn calculate(&self) -> f64 {
76        let mut total = self.base;
77        for modifier in self.modifiers_by_id.values() {
78            match modifier.operation {
79                AttributeModifierOperation::AddValue => total += modifier.amount,
80                AttributeModifierOperation::AddMultipliedBase => {
81                    total += modifier.amount * self.base
82                }
83                AttributeModifierOperation::AddMultipliedTotal => total *= 1. + modifier.amount,
84            };
85        }
86        total
87    }
88
89    /// Add a new modifier to this attribute and return the previous value, if
90    /// present.
91    pub fn insert(&mut self, modifier: AttributeModifier) -> Option<AttributeModifier> {
92        self.modifiers_by_id.insert(modifier.id.clone(), modifier)
93    }
94
95    /// Insert the given modifier if it's not already present, otherwise returns
96    /// [`AlreadyPresentError`].
97    pub fn try_insert(&mut self, modifier: AttributeModifier) -> Result<(), AlreadyPresentError> {
98        match self.modifiers_by_id.entry(modifier.id.clone()) {
99            hash_map::Entry::Occupied(_) => Err(AlreadyPresentError),
100            hash_map::Entry::Vacant(entry) => {
101                entry.insert(modifier);
102                Ok(())
103            }
104        }
105    }
106
107    /// Remove the modifier with the given ID from this attribute, returning
108    /// the previous modifier is present.
109    pub fn remove(&mut self, id: &Identifier) -> Option<AttributeModifier> {
110        self.modifiers_by_id.remove(id)
111    }
112}
113
114pub fn sprinting_modifier() -> AttributeModifier {
115    AttributeModifier {
116        id: Identifier::new("sprinting"),
117        amount: 0.3f32 as f64,
118        operation: AttributeModifierOperation::AddMultipliedTotal,
119    }
120}
121pub fn base_attack_speed_modifier(amount: f64) -> AttributeModifier {
122    AttributeModifier {
123        id: Identifier::new("base_attack_speed"),
124        amount,
125        operation: AttributeModifierOperation::AddValue,
126    }
127}
128pub fn creative_block_interaction_range_modifier() -> AttributeModifier {
129    AttributeModifier {
130        id: Identifier::new("creative_mode_block_range"),
131        amount: 0.5,
132        operation: AttributeModifierOperation::AddValue,
133    }
134}
135
136pub fn creative_entity_interaction_range_modifier() -> AttributeModifier {
137    AttributeModifier {
138        id: Identifier::new("creative_mode_entity_range"),
139        amount: 2.0,
140        operation: AttributeModifierOperation::AddValue,
141    }
142}