From 784459095835d4b28003cb7c39bed5bf9ab6492b Mon Sep 17 00:00:00 2001 From: "Eshan I." <2027eiyer@tjhsst.edu> Date: Sun, 2 Aug 2026 14:07:35 -0400 Subject: [PATCH] fix(ai): damage endermen in rain (#2714) --- pumpkin-util/src/biome.rs | 34 +++++++++++++++++++++++++++++- pumpkin/src/entity/mob/enderman.rs | 8 +++++-- pumpkin/src/world/mod.rs | 23 ++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/pumpkin-util/src/biome.rs b/pumpkin-util/src/biome.rs index 81ef4e659..d2bbbf99c 100644 --- a/pumpkin-util/src/biome.rs +++ b/pumpkin-util/src/biome.rs @@ -67,7 +67,6 @@ impl TemperatureModifier { /// Represents weather information for a biome, including temperature and precipitation. #[derive(Clone, Debug)] pub struct Weather { - #[expect(dead_code)] has_precipitation: bool, /// Base temperature of the biome. temperature: f32, @@ -136,4 +135,37 @@ impl Weather { modified_temperature } } + + #[must_use] + pub fn warm_enough_to_rain(&self, x: i32, y: i32, z: i32, sea_level: i32) -> bool { + self.compute_temperature(f64::from(x), y, f64::from(z), sea_level) >= 0.15 + } + + #[must_use] + pub fn is_rain_at(&self, x: i32, y: i32, z: i32, sea_level: i32) -> bool { + self.has_precipitation && self.warm_enough_to_rain(x, y, z, sea_level) + } +} + +#[cfg(test)] +mod tests { + use super::{TemperatureModifier, Weather}; + + #[test] + fn precipitation_controls_rain() { + let weather = Weather::new(false, 1.0, TemperatureModifier::None, 0.0); + assert!(!weather.is_rain_at(0, 64, 0, 63)); + } + + #[test] + fn warm_precipitation_is_rain() { + let weather = Weather::new(true, 1.0, TemperatureModifier::None, 0.0); + assert!(weather.is_rain_at(0, 64, 0, 63)); + } + + #[test] + fn cold_precipitation_is_snow() { + let weather = Weather::new(true, 0.0, TemperatureModifier::None, 0.0); + assert!(!weather.is_rain_at(0, 64, 0, 63)); + } } diff --git a/pumpkin/src/entity/mob/enderman.rs b/pumpkin/src/entity/mob/enderman.rs index 1f78fb453..54cb63a6e 100644 --- a/pumpkin/src/entity/mob/enderman.rs +++ b/pumpkin/src/entity/mob/enderman.rs @@ -427,8 +427,12 @@ impl Mob for EndermanEntity { return; } - // TODO: also check rain - if entity.touching_water.load(Ordering::SeqCst) { + let world = entity.world.load(); + let raining_at_feet = world.is_raining_at(&entity.block_pos.load()).await; + let raining_at_head = world + .is_raining_at(&entity.bounding_box.load().max_block_pos()) + .await; + if entity.touching_water.load(Ordering::SeqCst) || raining_at_feet || raining_at_head { self.mob_entity .living_entity .damage_with_context(self, 1.0, DamageType::DROWN, None, None, None) diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index f885564ec..d7dada934 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -162,6 +162,8 @@ use weather::Weather; type FlowingFluidProperties = pumpkin_data::fluid::FlowingWaterLikeFluidProperties; +const MAX_LIGHT_LEVEL: u8 = 15; + use rustc_hash::{FxHashMap, FxHashSet}; impl PumpkinError for GetBlockError { @@ -1730,6 +1732,20 @@ impl World { self.weather.lock().await.raining } + pub async fn is_raining_at(&self, pos: &BlockPos) -> bool { + if !self.is_raining().await { + return false; + } + if self.get_heightmap_height(MotionBlocking, pos.0.x, pos.0.z) + 1 > pos.0.y { + return false; + } + self.can_see_sky(pos) + && self + .get_biome(pos) + .weather + .is_rain_at(pos.0.x, pos.0.y, pos.0.z, self.sea_level) + } + pub async fn set_raining(&self, raining: bool) { let mut weather = self.weather.lock().await; if weather.raining != raining { @@ -4439,6 +4455,13 @@ impl World { .get_sky_light_level(&self.level, position) } + #[must_use] + pub fn can_see_sky(&self, position: &BlockPos) -> bool { + position.0.y >= self.dimension.min_y + && position.0.y < self.dimension.min_y + self.dimension.height + && self.get_sky_light_level(position) >= MAX_LIGHT_LEVEL + } + pub fn set_block_light_level(&self, position: &BlockPos, light_level: u8) { let _ = self .level