chore: more entities taking sun damage

instead of hardcoding, it now uses the tag::EntityType::MINECRAFT_BURN_IN_DAYLIGHT, will definitely be more future proof as well
This commit is contained in:
Alexander Medvedev 2026-05-02 22:04:26 +02:00
parent 9c0adaa576
commit 755a1bd749
7 changed files with 72 additions and 112 deletions

View File

@ -8,9 +8,9 @@ use crate::world::World;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::attributes::Attributes; use pumpkin_data::attributes::Attributes;
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use pumpkin_data::data_component_impl::EquipmentSlot;
use pumpkin_data::item_stack::ItemStack; use pumpkin_data::item_stack::ItemStack;
use pumpkin_data::meta_data_type::MetaDataType; use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tag::{self, Taggable};
use pumpkin_data::tracked_data::TrackedData; use pumpkin_data::tracked_data::TrackedData;
use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot, Metadata}; use pumpkin_protocol::java::client::play::{CHeadRot, CUpdateEntityRot, Metadata};
use pumpkin_util::math::boundingbox::BoundingBox; use pumpkin_util::math::boundingbox::BoundingBox;
@ -240,6 +240,68 @@ impl MobEntity {
base_box.expand(attack_range, 0.0, attack_range) base_box.expand(attack_range, 0.0, attack_range)
} }
pub async fn tick_sun_burn(&self) {
if !self
.living_entity
.entity
.entity_type
.has_tag(&tag::EntityType::MINECRAFT_BURN_IN_DAYLIGHT)
{
return;
}
if !self.is_sun_burn_tick().await {
return;
}
self.apply_sun_burn();
}
async fn is_sun_burn_tick(&self) -> bool {
let entity = &self.living_entity.entity;
let world_arc = entity.world.load();
let world = world_arc.as_ref();
// TODO: gate behind EnvironmentAttributes::MONSTERS_BURN once implemented.
// Vanilla: getLightLevelDependentMagicValue() — sky light at eye pos, scaled 0–1.
let eye_block_pos = entity.get_eye_pos();
let brightness = world
.level
.light_engine
.get_sky_light_level(&world.level, &eye_block_pos.to_block_pos())
.await as f32
/ 15.0;
if brightness <= 0.5 {
return false;
}
let is_in_non_burnable = entity.touching_water.load(Relaxed)
|| world.weather.lock().await.raining
|| entity.is_in_powder_snow()
|| entity.was_in_powder_snow.load(Relaxed);
if is_in_non_burnable {
return false;
}
let pos = entity.pos.load();
let top_y = world
.get_top_block(Vector2::new(pos.x as i32, pos.z as i32))
.await;
if (entity.get_eye_y() as i32) < top_y {
return false;
}
let mut rng = rand::rng();
rng.random::<f32>() * 30.0 < (brightness - 0.4) * 2.0
}
fn apply_sun_burn(&self) {
let entity = &self.living_entity.entity;
entity.set_on_fire_for(8.0);
}
} }
pub trait Mob: EntityBase + Send + Sync { pub trait Mob: EntityBase + Send + Sync {
@ -348,6 +410,7 @@ impl<T: Mob + Send + 'static> EntityBase for T {
) -> EntityBaseFuture<'a, ()> { ) -> EntityBaseFuture<'a, ()> {
Box::pin(async move { Box::pin(async move {
let mob_entity = self.get_mob_entity(); let mob_entity = self.get_mob_entity();
mob_entity.tick_sun_burn().await;
if mob_entity.breeding_cooldown.load(Relaxed) > 0 { if mob_entity.breeding_cooldown.load(Relaxed) > 0 {
mob_entity.breeding_cooldown.fetch_sub(1, Relaxed); mob_entity.breeding_cooldown.fetch_sub(1, Relaxed);
@ -578,66 +641,3 @@ pub trait PathAwareEntity: Mob + Send + Sync {
1.0 1.0
} }
} }
pub trait SunSensitive: Mob + Send + Sync {
fn sun_sensitive_tick(&self) -> EntityBaseFuture<'_, ()> {
Box::pin(async {
let mob = self.get_mob_entity();
let entity = &mob.living_entity.entity;
if !entity.is_alive()
|| entity.touching_water.load(Relaxed)
|| entity.is_in_powder_snow()
{
return;
}
let world_arc = entity.world.load();
let world = world_arc.as_ref();
if world.level_time.lock().await.is_night() {
return;
}
if world.weather.lock().await.raining {
return;
}
let pos = entity.pos.load();
let top_y = world
.get_top_block(Vector2::new(pos.x as i32, pos.z as i32))
.await;
if (entity.get_eye_y() as i32) < top_y {
return;
}
let brightness = world
.level
.light_engine
.get_sky_light_level(&world.level, &pos.to_block_pos())
.await as f32
/ 15.0;
if brightness < 0.5 {
return;
}
let damage_amount = {
let mut rng = self.get_random();
if rng.random::<f32>() * 30.0 >= (brightness - 0.4) * 2.0 {
return;
}
rng.random_range(0..=1i32)
};
let equipment = mob.living_entity.entity_equipment.lock().await;
let head_slot = equipment.get(&EquipmentSlot::HEAD);
let mut head_item = head_slot.lock().await;
if head_item.is_empty() {
entity.set_on_fire_for(8.0);
} else {
// TODO: Handle DamageResult::Broken to broadcast item break and update player slot.
let _ = head_item.damage_item(damage_amount);
}
})
}
}

View File

@ -1,6 +1,5 @@
use crate::entity::mob::SunSensitive;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, Entity, NBTStorage,
mob::{Mob, MobEntity, skeleton::SkeletonEntityBase}, mob::{Mob, MobEntity, skeleton::SkeletonEntityBase},
}; };
use std::sync::Arc; use std::sync::Arc;
@ -23,10 +22,4 @@ impl Mob for BoggedSkeletonEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for BoggedSkeletonEntity {}

View File

@ -1,8 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::entity::mob::SunSensitive;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, Entity, NBTStorage,
mob::{Mob, MobEntity, skeleton::SkeletonEntityBase}, mob::{Mob, MobEntity, skeleton::SkeletonEntityBase},
}; };
@ -24,10 +23,4 @@ impl Mob for SkeletonEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for SkeletonEntity {}

View File

@ -1,8 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::entity::mob::SunSensitive;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, Entity, NBTStorage,
mob::{Mob, MobEntity, skeleton::SkeletonEntityBase}, mob::{Mob, MobEntity, skeleton::SkeletonEntityBase},
}; };
@ -24,10 +23,4 @@ impl Mob for StraySkeletonEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for StraySkeletonEntity {}

View File

@ -1,9 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use crate::entity::mob::SunSensitive;
use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::mob::zombie::ZombieEntityBase;
use crate::entity::{ use crate::entity::{
Entity, EntityBase, EntityBaseFuture, NBTStorage, Entity, NBTStorage,
mob::{Mob, MobEntity}, mob::{Mob, MobEntity},
}; };
@ -41,10 +40,4 @@ impl Mob for DrownedEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for DrownedEntity {}

View File

@ -1,6 +1,6 @@
use crate::entity::NBTStorage;
use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::mob::zombie::ZombieEntityBase;
use crate::entity::mob::{Mob, MobEntity, SunSensitive}; use crate::entity::mob::{Mob, MobEntity};
use crate::entity::{EntityBase, EntityBaseFuture, NBTStorage};
use std::sync::Arc; use std::sync::Arc;
pub struct ZombieEntity { pub struct ZombieEntity {
@ -21,10 +21,4 @@ impl Mob for ZombieEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.entity.mob_entity &self.entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for ZombieEntity {}

View File

@ -1,6 +1,6 @@
use crate::entity::mob::zombie::ZombieEntityBase; use crate::entity::mob::zombie::ZombieEntityBase;
use crate::entity::mob::{Mob, MobEntity, SunSensitive}; use crate::entity::mob::{Mob, MobEntity};
use crate::entity::{Entity, EntityBase, EntityBaseFuture, NBTStorage}; use crate::entity::{Entity, NBTStorage};
use std::sync::Arc; use std::sync::Arc;
pub struct ZombieVillagerEntity { pub struct ZombieVillagerEntity {
@ -21,10 +21,4 @@ impl Mob for ZombieVillagerEntity {
fn get_mob_entity(&self) -> &MobEntity { fn get_mob_entity(&self) -> &MobEntity {
&self.mob_entity.mob_entity &self.mob_entity.mob_entity
} }
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
self.sun_sensitive_tick()
}
} }
impl SunSensitive for ZombieVillagerEntity {}