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