aboutsummaryrefslogtreecommitdiff
path: root/azalea-entity/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-entity/src')
-rw-r--r--azalea-entity/src/effects.rs131
-rw-r--r--azalea-entity/src/plugin/effect_events.rs58
-rw-r--r--azalea-entity/src/plugin/mod.rs4
3 files changed, 184 insertions, 9 deletions
diff --git a/azalea-entity/src/effects.rs b/azalea-entity/src/effects.rs
index 72c757d6..9ce15d88 100644
--- a/azalea-entity/src/effects.rs
+++ b/azalea-entity/src/effects.rs
@@ -4,17 +4,22 @@ use std::{
};
use azalea_buf::{AzBuf, BufReadError};
-use azalea_core::bitset::FixedBitSet;
-use azalea_registry::builtin::MobEffect;
+use azalea_core::{attribute_modifier_operation::AttributeModifierOperation, bitset::FixedBitSet};
+use azalea_inventory::components::AttributeModifier;
+use azalea_registry::{
+ builtin::{Attribute, MobEffect},
+ identifier::Identifier,
+};
/// Data about an active mob effect.
#[derive(AzBuf, Clone, Debug, Default, PartialEq)]
pub struct MobEffectData {
/// The effect's amplifier level, starting at 0 if present.
#[var]
- pub amplifier: u32,
+ pub amplifier: i32,
+ /// The effect's duration in ticks.
#[var]
- pub duration_ticks: u32,
+ pub duration: i32,
pub flags: MobEffectFlags,
}
@@ -64,8 +69,8 @@ impl AzBuf for MobEffectFlags {
#[derive(Clone, Debug, Default)]
pub struct ActiveEffects(pub HashMap<MobEffect, MobEffectData>);
impl ActiveEffects {
- pub fn insert(&mut self, effect: MobEffect, data: MobEffectData) {
- self.0.insert(effect, data);
+ pub fn insert(&mut self, effect: MobEffect, data: MobEffectData) -> Option<MobEffectData> {
+ self.0.insert(effect, data)
}
pub fn remove(&mut self, effect: MobEffect) -> Option<MobEffectData> {
@@ -73,7 +78,7 @@ impl ActiveEffects {
}
/// Get the amplifier level for the effect, starting at 0.
- pub fn get_level(&self, effect: MobEffect) -> Option<u32> {
+ pub fn get_level(&self, effect: MobEffect) -> Option<i32> {
self.0.get(&effect).map(|data| data.amplifier)
}
@@ -82,7 +87,7 @@ impl ActiveEffects {
}
/// Returns the amplifier for dig speed (haste / conduit power), if present.
- pub fn get_dig_speed_amplifier(&self) -> Option<u32> {
+ pub fn get_dig_speed_amplifier(&self) -> Option<i32> {
let haste_level = self
.get_level(MobEffect::Haste)
.map(|level| level + 1)
@@ -92,7 +97,7 @@ impl ActiveEffects {
.map(|level| level + 1)
.unwrap_or_default();
- let effect_plus_one = u32::max(haste_level, conduit_power_level);
+ let effect_plus_one = i32::max(haste_level, conduit_power_level);
if effect_plus_one > 0 {
Some(effect_plus_one - 1)
} else {
@@ -100,3 +105,111 @@ impl ActiveEffects {
}
}
}
+
+pub fn attribute_modifier_for_effect(id: MobEffect) -> Option<(Attribute, AttributeTemplate)> {
+ Some(match id {
+ MobEffect::Speed => (
+ Attribute::MovementSpeed,
+ AttributeTemplate::new(
+ "effect.speed",
+ 0.2f32 as f64,
+ AttributeModifierOperation::AddMultipliedTotal,
+ ),
+ ),
+ MobEffect::Slowness => (
+ Attribute::MovementSpeed,
+ AttributeTemplate::new(
+ "effect.slowness",
+ -0.15f32 as f64,
+ AttributeModifierOperation::AddMultipliedTotal,
+ ),
+ ),
+ MobEffect::Haste => (
+ Attribute::AttackSpeed,
+ AttributeTemplate::new(
+ "effect.haste",
+ 0.1f32 as f64,
+ AttributeModifierOperation::AddMultipliedTotal,
+ ),
+ ),
+ MobEffect::MiningFatigue => (
+ Attribute::AttackSpeed,
+ AttributeTemplate::new(
+ "effect.mining_fatigue",
+ -0.1f32 as f64,
+ AttributeModifierOperation::AddMultipliedTotal,
+ ),
+ ),
+ MobEffect::Strength => (
+ Attribute::AttackDamage,
+ AttributeTemplate::new("effect.strength", 3.0, AttributeModifierOperation::AddValue),
+ ),
+ MobEffect::JumpBoost => (
+ Attribute::SafeFallDistance,
+ AttributeTemplate::new(
+ "effect.jump_boost",
+ 1.0,
+ AttributeModifierOperation::AddValue,
+ ),
+ ),
+ MobEffect::Invisibility => (
+ Attribute::WaypointTransmitRange,
+ AttributeTemplate::new(
+ "effect.waypoint_transmit_range_hide",
+ -1.0,
+ AttributeModifierOperation::AddMultipliedTotal,
+ ),
+ ),
+ MobEffect::Weakness => (
+ Attribute::AttackDamage,
+ AttributeTemplate::new(
+ "effect.weakness",
+ -4.0,
+ AttributeModifierOperation::AddValue,
+ ),
+ ),
+ MobEffect::HealthBoost => (
+ Attribute::MaxHealth,
+ AttributeTemplate::new(
+ "effect.health_boost",
+ 4.0,
+ AttributeModifierOperation::AddValue,
+ ),
+ ),
+ MobEffect::Absorption => (
+ Attribute::MaxAbsorption,
+ AttributeTemplate::new(
+ "effect.absorption",
+ 4.0,
+ AttributeModifierOperation::AddValue,
+ ),
+ ),
+ MobEffect::Luck => (
+ Attribute::Luck,
+ AttributeTemplate::new("effect.luck", 1.0, AttributeModifierOperation::AddValue),
+ ),
+ MobEffect::Unluck => (
+ Attribute::Luck,
+ AttributeTemplate::new("effect.unluck", -1.0, AttributeModifierOperation::AddValue),
+ ),
+ _ => return None,
+ })
+}
+
+pub struct AttributeTemplate(AttributeModifier);
+impl AttributeTemplate {
+ pub fn new(id: &str, amount: f64, operation: AttributeModifierOperation) -> Self {
+ Self(AttributeModifier {
+ id: Identifier::from(id),
+ amount,
+ operation,
+ })
+ }
+ pub fn create(self, amplifier: i32) -> AttributeModifier {
+ AttributeModifier {
+ id: self.0.id,
+ amount: self.0.amount * (amplifier + 1) as f64,
+ operation: self.0.operation,
+ }
+ }
+}
diff --git a/azalea-entity/src/plugin/effect_events.rs b/azalea-entity/src/plugin/effect_events.rs
new file mode 100644
index 00000000..2d37c463
--- /dev/null
+++ b/azalea-entity/src/plugin/effect_events.rs
@@ -0,0 +1,58 @@
+use azalea_registry::builtin::MobEffect;
+use bevy_ecs::{entity::Entity, event::EntityEvent, observer::On, system::Query};
+use tracing::warn;
+
+use crate::{ActiveEffects, Attributes, MobEffectData, effects::attribute_modifier_for_effect};
+
+#[derive(EntityEvent)]
+pub struct AddEffectEvent {
+ pub entity: Entity,
+ pub id: MobEffect,
+ pub data: MobEffectData,
+}
+pub fn handle_add_effect(
+ add_effect: On<AddEffectEvent>,
+ mut query: Query<(&mut ActiveEffects, &mut Attributes)>,
+) {
+ let Ok((mut active_effects, mut attributes)) = query.get_mut(add_effect.entity) else {
+ warn!("got handle_add_effect for an entity without the required components");
+ return;
+ };
+
+ active_effects.insert(add_effect.id, add_effect.data.clone());
+
+ if let Some((attribute, modifier)) = attribute_modifier_for_effect(add_effect.id) {
+ let modifier = modifier.create(add_effect.data.amplifier);
+ if let Some(attribute) = attributes.get_mut(attribute) {
+ attribute.insert(modifier);
+ }
+ }
+}
+
+#[derive(EntityEvent)]
+pub struct RemoveEffectsEvent {
+ pub entity: Entity,
+ pub effects: Vec<MobEffect>,
+}
+pub fn handle_remove_effects(
+ remove_effects: On<RemoveEffectsEvent>,
+ mut query: Query<(&mut ActiveEffects, &mut Attributes)>,
+) {
+ let Ok((mut active_effects, mut attributes)) = query.get_mut(remove_effects.entity) else {
+ warn!("got handle_remove_effects for an entity without the required components");
+ return;
+ };
+
+ for &effect in &remove_effects.effects {
+ active_effects.remove(effect);
+
+ if let Some((attribute, modifier)) = attribute_modifier_for_effect(effect) {
+ // we're just trying to get the id of the modifier, so the amplifier passed here
+ // doesn't matter
+ let modifier = modifier.create(0);
+ if let Some(attribute) = attributes.get_mut(attribute) {
+ attribute.remove(&modifier.id);
+ }
+ }
+ }
+}
diff --git a/azalea-entity/src/plugin/mod.rs b/azalea-entity/src/plugin/mod.rs
index 6035f674..4d97080f 100644
--- a/azalea-entity/src/plugin/mod.rs
+++ b/azalea-entity/src/plugin/mod.rs
@@ -1,4 +1,5 @@
mod components;
+pub mod effect_events;
pub mod indexing;
use std::collections::HashSet;
@@ -22,6 +23,7 @@ use crate::{
FluidOnEyes, LookDirection, Physics, Pose, Position,
dimensions::{EntityDimensions, calculate_dimensions},
metadata::{self, Health, Player},
+ plugin::effect_events::{handle_add_effect, handle_remove_effects},
};
/// A Bevy [`SystemSet`] for various types of entity updates.
@@ -65,6 +67,8 @@ impl Plugin for EntityPlugin {
),
)
.add_systems(GameTick, (update_in_loaded_chunk, update_fluid_on_eyes))
+ .add_observer(handle_add_effect)
+ .add_observer(handle_remove_effects)
.init_resource::<EntityUuidIndex>();
}
}