Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pumpkin-data/build/entity_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub(crate) fn build() -> TokenStream {
impl Taggable for EntityType {
#[inline]
fn tag_key() -> RegistryKey {
RegistryKey::Block
RegistryKey::EntityType
}

#[inline]
Expand Down
69 changes: 68 additions & 1 deletion pumpkin/src/entity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use pumpkin_data::block_properties::{EnumVariants, Integer0To15};
use pumpkin_data::dimension::Dimension;
use pumpkin_data::fluid::Fluid;
use pumpkin_data::meta_data_type::MetaDataType;
use pumpkin_data::tag::Taggable;
use pumpkin_data::tracked_data::TrackedData;
use pumpkin_data::{Block, BlockDirection};
use pumpkin_data::{
Expand Down Expand Up @@ -365,6 +366,9 @@ pub struct Entity {
pub damage_immunities: Vec<DamageType>,
pub fire_ticks: AtomicI32,
pub has_visual_fire: AtomicBool,
/// The number of ticks the entity has been frozen (in powder snow)
/// Max is 140 ticks (7 seconds). Increases by 1/tick in powder snow, decreases by 2/tick outside.
pub frozen_ticks: AtomicI32,
pub removal_reason: AtomicCell<Option<RemovalReason>>,
// The passengers that entity has
pub passengers: Mutex<Vec<Arc<dyn EntityBase>>>,
Expand Down Expand Up @@ -461,6 +465,7 @@ impl Entity {
data: AtomicI32::new(0),
fire_ticks: AtomicI32::new(-1),
has_visual_fire: AtomicBool::new(false),
frozen_ticks: AtomicI32::new(0),
removal_reason: AtomicCell::new(None),
passengers: Mutex::new(Vec::new()),
vehicle: Mutex::new(None),
Expand Down Expand Up @@ -1578,6 +1583,66 @@ impl Entity {
// TODO: defrost
}

/// Maximum freeze ticks (7 seconds at 20 tps)
pub const MAX_FROZEN_TICKS: i32 = 140;

/// Freeze damage is dealt every 40 ticks when fully frozen
const FREEZE_DAMAGE_INTERVAL: i32 = 40;

/// Check if the entity is currently in powder snow
pub async fn is_in_powder_snow(&self) -> bool {
let block_pos = self.block_pos.load();
self.world.load().get_block(&block_pos).await == &Block::POWDER_SNOW
}

/// Check if this entity type is immune to freezing
/// Uses the `minecraft:freeze_immune_entity_types` tag
pub fn is_freeze_immune(&self) -> bool {
self.entity_type
.is_tagged_with("minecraft:freeze_immune_entity_types")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of this you should use .has_tag(&tag::EntityType::MINECRAFT_FREEZE_IMMUNE_ENTITY_TYPES)

.unwrap_or(false)
}

/// Ticks the frozen state of the entity.
/// In powder snow: `frozen_ticks` increases by 1 (up to `MAX_FROZEN_TICKS`)
/// Outside powder snow: `frozen_ticks` decreases by 2 (down to 0)
/// When fully frozen, deals 1 damage every 40 ticks
pub async fn tick_frozen(&self, caller: &dyn EntityBase) {
// Freeze-immune entities don't accumulate freeze ticks
if self.is_freeze_immune() {
return;
}

let in_powder_snow = self.is_in_powder_snow().await;
let old_frozen_ticks = self.frozen_ticks.load(Ordering::Relaxed);

let new_frozen_ticks = if in_powder_snow {
// Increase frozen ticks when in powder snow
(old_frozen_ticks + 1).min(Self::MAX_FROZEN_TICKS)
} else {
// Decrease frozen ticks when not in powder snow (2x faster thaw rate)
(old_frozen_ticks - 2).max(0)
};

// Only update and send metadata if the value changed
if new_frozen_ticks != old_frozen_ticks {
self.frozen_ticks.store(new_frozen_ticks, Ordering::Relaxed);
self.send_meta_data(&[Metadata::new(
TrackedData::DATA_FROZEN_TICKS,
MetaDataType::Integer,
VarInt(new_frozen_ticks),
)])
.await;
}

// Deal freeze damage when fully frozen (every 40 ticks)
if new_frozen_ticks >= Self::MAX_FROZEN_TICKS
&& self.age.load(Ordering::Relaxed) % Self::FREEZE_DAMAGE_INTERVAL == 0
{
caller.damage(caller, 1.0, DamageType::FREEZE).await;
}
}

/// Sets the `Entity` yaw & pitch rotation
pub fn set_rotation(&self, yaw: f32, pitch: f32) {
// TODO
Expand Down Expand Up @@ -2102,7 +2167,9 @@ impl EntityBase for Entity {
}
self.set_on_fire(self.fire_ticks.load(Ordering::Relaxed) > 0)
.await;
// TODO: Tick

// Tick freeze state (powder snow)
self.tick_frozen(&*caller).await;
})
}

Expand Down