Pumpkin/crates/pumpkin/src/enchantment/effects/apply_mob_effect.rs
Alexander Medvedev 3f88eeaba7 feat: implement missing effects
also migrate loot tables from our own extractor to datapack
2026-09-01 14:16:05 +02:00

114 lines
3.4 KiB
Rust

use std::sync::Arc;
use pumpkin_data::effect::StatusEffect;
use pumpkin_data::enchantment::LevelBasedValue;
use pumpkin_data::potion::Effect;
use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::random::{RandomGenerator, RandomImpl, get_seed, xoroshiro128::Xoroshiro};
use super::EnchantmentEntityEffectExt;
use crate::entity::Entity;
use crate::entity::player::Player;
use crate::world::World;
/// Enchantment entity effect that applies a status effect.
#[derive(Clone, Debug, PartialEq)]
pub struct ApplyMobEffect {
pub to_apply: Vec<&'static StatusEffect>,
pub min_duration: LevelBasedValue,
pub max_duration: LevelBasedValue,
pub min_amplifier: LevelBasedValue,
pub max_amplifier: LevelBasedValue,
}
impl ApplyMobEffect {
#[must_use]
pub const fn new(
to_apply: Vec<&'static StatusEffect>,
min_duration: LevelBasedValue,
max_duration: LevelBasedValue,
min_amplifier: LevelBasedValue,
max_amplifier: LevelBasedValue,
) -> Self {
Self {
to_apply,
min_duration,
max_duration,
min_amplifier,
max_amplifier,
}
}
pub fn apply(
&self,
world: &Arc<World>,
enchantment_level: i32,
_owner: Option<&Arc<Player>>,
entity: Option<&Entity>,
) {
if self.to_apply.is_empty() {
return;
}
let Some(entity) = entity else {
return;
};
let mut rng = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(get_seed()));
let selected_idx = (rng.next_f32() * self.to_apply.len() as f32) as usize;
let selected = self.to_apply[selected_idx.min(self.to_apply.len() - 1)];
let min_dur = self.min_duration.calculate(enchantment_level);
let max_dur = self.max_duration.calculate(enchantment_level);
let dur_secs = if (max_dur - min_dur).abs() < f32::EPSILON || min_dur >= max_dur {
min_dur
} else {
rng.next_f32().mul_add(max_dur - min_dur, min_dur)
};
let ticks = (dur_secs * 20.0).round() as i32;
let min_amp = self.min_amplifier.calculate(enchantment_level);
let max_amp = self.max_amplifier.calculate(enchantment_level);
let amp_f = if (max_amp - min_amp).abs() < f32::EPSILON || min_amp >= max_amp {
min_amp
} else {
rng.next_f32().mul_add(max_amp - min_amp, min_amp)
};
let amplifier = (amp_f.round() as i32).max(0) as u8;
let effect = Effect {
effect_type: selected,
duration: ticks,
amplifier,
ambient: false,
show_particles: true,
show_icon: true,
blend: false,
};
let target_player = world.get_player_by_id(entity.entity_id);
if let Some(target_player) = target_player {
target_player.add_effect(effect);
} else if let Some(living) = world
.get_entity_by_id(entity.entity_id)
.as_deref()
.and_then(crate::entity::EntityBase::get_living_entity)
{
living.add_effect(effect);
}
}
}
impl EnchantmentEntityEffectExt for ApplyMobEffect {
fn apply(
&self,
world: &Arc<World>,
enchantment_level: i32,
owner: Option<&Arc<Player>>,
entity: Option<&Entity>,
_position: Vector3<f64>,
) {
self.apply(world, enchantment_level, owner, entity);
}
}