1use steel_math::{DEGREE_90, trig};
2use steel_registry::data_components::vanilla_components::{DEATH_PROTECTION, USE_EFFECTS};
3use steel_registry::stat::vanilla_stat_types;
4use steel_registry::{DyeColor, vanilla_custom_stats};
5
6use super::*;
7use crate::behavior::{BLOCK_BEHAVIORS, InventoryTickContext, MOB_EFFECT_BEHAVIORS};
8use crate::entity::consume_effect::apply_consume_effect;
9use crate::entity::damage::RecentDamageSource;
10use crate::entity::pushing::EntityPush;
11
12const BED_SLEEP_POSITION_OFFSET: f64 = 0.125;
13
14pub trait LivingEntity: Entity {
22 fn living_base(&self) -> &LivingEntityBase;
26
27 fn living_rotation_state(&self) -> LivingRotationState {
29 self.living_base().rotation_state()
30 }
31
32 fn y_body_rot(&self) -> f32 {
34 self.living_base().y_body_rot()
35 }
36
37 fn set_y_body_rot(&self, y_body_rot: f32) {
39 self.living_base().set_y_body_rot(y_body_rot);
40 }
41
42 fn y_head_rot(&self) -> f32 {
44 self.living_base().y_head_rot()
45 }
46
47 fn set_y_head_rot(&self, y_head_rot: f32) {
49 self.living_base().set_y_head_rot(y_head_rot);
50 }
51
52 fn advance_living_rotation_for_base_tick(&self) {
54 self.living_base().advance_rotation_for_base_tick();
55 }
56
57 fn base_tick_living_entity(&self) {
62 self.advance_living_rotation_for_base_tick();
63 self.living_base().tick_swing_state();
64 self.entity_base_tick();
65 self.tick_living_environmental_damage();
66 self.living_base().decrement_hurt_time();
67 self.living_base().tick_last_hurt_by_player_memory();
68 self.living_base()
69 .tick_living_combat_memory(self.tick_count());
70 }
71
72 fn living_swing_state(&self) -> LivingSwingState {
74 self.living_base().swing_state()
75 }
76
77 fn modified_swing_duration(&self, animation: SwingAnimation) -> i32 {
79 let swing_duration = animation.duration;
80 if let Some(haste) = self.mob_effect(vanilla_mob_effects::HASTE) {
81 swing_duration - (1 + haste.amplifier())
82 } else if let Some(mining_fatigue) = self.mob_effect(vanilla_mob_effects::MINING_FATIGUE) {
83 swing_duration + (1 + mining_fatigue.amplifier()) * 2
84 } else {
85 swing_duration
86 }
87 }
88
89 fn attack_animation(&self, hand: InteractionHand) -> SwingAnimation {
91 self.held_swing_animation(hand, ATTACK_ANIMATION)
92 }
93
94 fn interact_animation(&self, hand: InteractionHand) -> SwingAnimation {
96 self.held_swing_animation(hand, INTERACT_ANIMATION)
97 }
98
99 fn held_swing_animation(
102 &self,
103 hand: InteractionHand,
104 component: DataComponentType<SwingAnimation>,
105 ) -> SwingAnimation {
106 let slot = match hand {
107 InteractionHand::MainHand => EquipmentSlot::MainHand,
108 InteractionHand::OffHand => EquipmentSlot::OffHand,
109 };
110 let mut animation = SwingAnimation::DEFAULT;
111 self.with_equipment_slot(slot, &mut |item_stack| {
112 animation = item_stack
113 .get(component.clone())
114 .copied()
115 .unwrap_or(SwingAnimation::DEFAULT);
116 });
117 animation
118 }
119
120 fn swing(&self, hand: InteractionHand, animation: SwingAnimation, update_self: bool) -> bool {
122 if !self
123 .living_base()
124 .start_swing(hand, animation, self.modified_swing_duration(animation))
125 {
126 return false;
127 }
128
129 let Some(world) = self.level() else {
130 return true;
131 };
132
133 let packet = CSwingAnimation::new(self.id(), hand, animation);
134 let exclude = if update_self { None } else { Some(self.id()) };
135 world.broadcast_to_entity_trackers(self.id(), packet.clone(), exclude);
136 if update_self && let Some(player) = self.as_player() {
137 player.send_packet(packet);
138 }
139
140 true
141 }
142
143 fn swing_for_attack(&self, hand: InteractionHand) {
145 self.swing(hand, self.attack_animation(hand), false);
146 }
147
148 fn attributes(&self) -> &SyncMutex<AttributeMap> {
150 self.living_base().attributes()
151 }
152
153 fn get_luck(&self) -> f32 {
155 0.0
156 }
157
158 fn pack_syncable_attributes(&self) -> Vec<AttributeSnapshot> {
163 self.attributes().lock().syncable_snapshots()
164 }
165
166 fn drain_dirty_syncable_attributes(&self) -> Vec<AttributeSnapshot> {
171 self.attributes().lock().drain_dirty_sync()
172 }
173
174 fn drain_dirty_mob_effects(&self) -> Vec<MobEffectSyncChange> {
176 self.living_base().drain_dirty_mob_effects()
177 }
178
179 fn pack_all_equipment(&self) -> Vec<EquipmentSlotItem> {
181 self.pack_living_equipment()
182 }
183
184 fn drain_dirty_equipment(&self) -> Vec<EquipmentSlotItem> {
186 self.drain_dirty_living_equipment()
187 }
188
189 fn save_equipment(&self, nbt: &mut NbtCompound) {
191 let mut equipment = NbtCompound::new();
192 for slot in EquipmentSlot::ALL {
193 self.with_equipment_slot(slot, &mut |item| {
194 if !item.is_empty() {
195 equipment.insert(slot.name(), item.to_nbt_tag_ref());
196 }
197 });
198 }
199 if !equipment.is_empty() {
200 nbt.insert("equipment", NbtTag::Compound(equipment));
201 }
202 }
203
204 fn load_equipment(&self, nbt: BorrowedNbtCompoundView<'_, '_>) {
206 if let Some(equipment_tag) = nbt.compound("equipment") {
207 let mut equipment = self.living_base().equipment().lock();
208 for slot in EquipmentSlot::ALL {
209 let item = equipment_tag
210 .get(slot.name())
211 .and_then(|tag| tag.compound())
212 .and_then(|comp| ItemStack::from_borrowed_compound(&comp))
213 .unwrap_or_else(ItemStack::empty);
214 equipment.set(slot, item);
215 }
216 }
217 for slot in EquipmentSlot::ALL {
218 self.refresh_equipment_attribute_modifiers(slot);
219 }
220 }
221
222 fn save_command_nbt(&self, nbt: &mut NbtCompound) {
224 nbt.insert("Health", self.get_health());
225 nbt.insert(
226 "HurtTime",
227 NbtTag::Short(self.living_base().hurt_time() as i16),
228 );
229 nbt.insert(
230 "DeathTime",
231 NbtTag::Short(self.living_base().death_time() as i16),
232 );
233 nbt.insert("AbsorptionAmount", self.get_absorption_amount());
234 nbt.insert(
235 "current_impulse_context_reset_grace_time",
236 self.living_base()
237 .current_impulse_context_reset_grace_time(),
238 );
239 if let Some(impact) = self.living_base().current_impulse_impact_pos() {
240 nbt.insert(
241 "current_explosion_impact_pos",
242 NbtList::Double(vec![impact.x, impact.y, impact.z]),
243 );
244 }
245 nbt.insert("attributes", self.attributes().lock().to_vanilla_nbt());
246
247 let mut effects = self.living_base().active_mob_effects();
248 effects.sort_by_key(|effect| effect.effect().try_id().unwrap_or(usize::MAX));
249 if !effects.is_empty() {
250 nbt.insert(
251 "active_effects",
252 NbtList::Compound(
253 effects
254 .iter()
255 .map(ActiveMobEffect::to_vanilla_nbt)
256 .collect(),
257 ),
258 );
259 }
260
261 nbt.insert("FallFlying", nbt_bool(self.is_fall_flying()));
262 if let Some(pos) = self.sleeping_pos() {
263 nbt.insert(
264 "sleeping_pos",
265 NbtTag::IntArray(vec![pos.x(), pos.y(), pos.z()]),
266 );
267 }
268 if let Some(uuid) = self.last_hurt_by_player_uuid() {
269 nbt.insert(
270 "last_hurt_by_player",
271 NbtTag::IntArray(uuid.to_int_array().to_vec()),
272 );
273 nbt.insert(
274 "last_hurt_by_player_memory_time",
275 self.last_hurt_by_player_memory_time(),
276 );
277 }
278 if let Some(entity) = self.last_hurt_by_mob() {
279 nbt.insert(
280 "last_hurt_by_mob",
281 NbtTag::IntArray(entity.uuid().to_int_array().to_vec()),
282 );
283 nbt.insert(
284 "ticks_since_last_hurt_by_mob",
285 self.tick_count()
286 .wrapping_sub(self.last_hurt_by_mob_timestamp()),
287 );
288 }
289
290 if self.as_mob().is_none() {
291 self.save_equipment(nbt);
292 }
293 }
294
295 fn get_health(&self) -> f32;
297
298 fn set_health(&self, health: f32);
300
301 fn get_max_health(&self) -> f32 {
303 self.attributes()
304 .lock()
305 .required_value(vanilla_attributes::MAX_HEALTH) as f32
306 }
307
308 fn no_action_time(&self) -> i32 {
310 self.living_base().no_action_time()
311 }
312
313 fn set_no_action_time(&self, no_action_time: i32) {
315 self.living_base().set_no_action_time(no_action_time);
316 }
317
318 fn increment_no_action_time(&self) {
320 self.living_base().increment_no_action_time();
321 }
322
323 fn heal(&self, amount: f32) {
325 let current_health = self.get_health();
326 if current_health > 0.0 {
327 self.set_health(current_health + amount);
328 }
329 }
330
331 fn is_dead_or_dying(&self) -> bool {
333 self.get_health() <= 0.0
334 }
335
336 fn is_baby(&self) -> bool {
338 self.as_ageable_mob().is_some_and(AgeableMob::is_baby)
339 }
340
341 fn sheep_loot_state(&self) -> Option<(DyeColor, bool)> {
347 None
348 }
349
350 fn chicken_loot_variant(&self) -> Option<&Identifier> {
355 None
356 }
357
358 fn sound_volume(&self) -> f32 {
360 1.0
361 }
362
363 fn voice_pitch(&self) -> f32 {
365 self.default_voice_pitch()
366 }
367
368 fn default_voice_pitch(&self) -> f32 {
370 if self.is_baby() {
371 (rand::random::<f32>() - rand::random::<f32>()) * 0.2 + 1.5
372 } else {
373 (rand::random::<f32>() - rand::random::<f32>()) * 0.2 + 1.0
374 }
375 }
376
377 fn hurt_sound(&self, _source: &DamageSource) -> Option<SoundEventRef> {
379 Some(&sound_events::ENTITY_GENERIC_HURT)
380 }
381
382 fn death_sound(&self) -> Option<SoundEventRef> {
384 Some(&sound_events::ENTITY_GENERIC_DEATH)
385 }
386
387 fn make_sound(&self, sound: Option<SoundEventRef>) {
389 if let Some(sound) = sound {
390 self.play_sound(sound, self.sound_volume(), self.voice_pitch());
391 }
392 }
393
394 fn play_hurt_sound(&self, source: &DamageSource) {
396 if let Some(mob) = self.as_mob() {
397 mob.reset_ambient_sound_time();
398 }
399 self.make_sound(self.hurt_sound(source));
400 }
401
402 fn play_death_sound(&self) {
404 self.make_sound(self.death_sound());
405 }
406
407 fn get_age_scale(&self) -> f32 {
409 if self.is_baby() { 0.5 } else { 1.0 }
410 }
411
412 fn get_scale(&self) -> f32 {
414 self.attributes()
415 .lock()
416 .get_value(vanilla_attributes::SCALE)
417 .unwrap_or(1.0) as f32
418 }
419
420 fn is_alive(&self) -> bool {
422 !self.is_dead_or_dying()
423 }
424
425 fn get_armor_cover_percentage(&self) -> f32 {
428 let mut covered_slots = 0;
429 for slot in EquipmentSlot::ARMOR_SLOTS {
430 self.with_equipment_slot(slot, &mut |item_stack| {
431 if !item_stack.is_empty() {
432 covered_slots += 1;
433 }
434 });
435 }
436
437 covered_slots as f32 / EquipmentSlot::ARMOR_SLOTS.len() as f32
438 }
439
440 fn get_visibility_percent(&self, targeting_entity: Option<&dyn Entity>) -> f64 {
443 let mut visibility_percent = 1.0;
444 if self.is_discrete() {
445 visibility_percent *= 0.8;
446 }
447
448 if self.is_invisible() {
449 visibility_percent *= 0.7 * f64::from(self.get_armor_cover_percentage().max(0.1));
450 }
451
452 if self.disguise_head_matches_targeting_entity(targeting_entity) {
453 visibility_percent *= 0.5;
454 }
455
456 visibility_percent
457 }
458
459 fn disguise_head_matches_targeting_entity(
461 &self,
462 targeting_entity: Option<&dyn Entity>,
463 ) -> bool {
464 let Some(targeting_entity) = targeting_entity else {
465 return false;
466 };
467
468 let mut matches_target = false;
469 self.with_equipment_slot(EquipmentSlot::Head, &mut |item_stack| {
470 let target_type = targeting_entity.entity_type();
471 matches_target = target_type == &vanilla_entities::SKELETON
472 && item_stack.is(&vanilla_items::SKELETON_SKULL)
473 || target_type == &vanilla_entities::ZOMBIE
474 && item_stack.is(&vanilla_items::ZOMBIE_HEAD)
475 || target_type == &vanilla_entities::PIGLIN
476 && item_stack.is(&vanilla_items::PIGLIN_HEAD)
477 || target_type == &vanilla_entities::PIGLIN_BRUTE
478 && item_stack.is(&vanilla_items::PIGLIN_HEAD)
479 || target_type == &vanilla_entities::CREEPER
480 && item_stack.is(&vanilla_items::CREEPER_HEAD);
481 });
482 matches_target
483 }
484
485 fn can_be_seen_by_anyone(&self) -> bool {
487 !self.is_spectator() && Entity::is_alive(self)
488 }
489
490 fn can_be_seen_as_enemy(&self) -> bool {
492 !self.is_invulnerable() && self.can_be_seen_by_anyone()
493 }
494
495 fn can_attack(&self, target: &dyn LivingEntity) -> bool {
498 if target.entity_type() == &vanilla_entities::PLAYER
499 && self
500 .level()
501 .is_some_and(|world| world.difficulty() == Difficulty::Peaceful)
502 {
503 return false;
504 }
505
506 target.can_be_seen_as_enemy()
507 }
508
509 fn last_damage_source(&self) -> Option<RecentDamageSource> {
512 self.base().damage_history().last_damage_source()
513 }
514
515 fn record_last_damage_source(&self, source: &DamageSource) {
517 let Some(world) = self.level() else {
518 panic!("recording living damage requires the victim's level");
519 };
520 let Some(history) = world.damage_history.upgrade() else {
521 panic!("recording living damage requires a live server damage history");
522 };
523 history.record(self.base(), source, &world.game_time);
524 }
525
526 fn set_last_hurt_by_player(&self, player_uuid: Uuid, time_to_remember: i32) {
528 self.living_base()
529 .set_last_hurt_by_player(player_uuid, time_to_remember);
530 }
531
532 fn invulnerable_time(&self) -> i32 {
534 self.living_base().invulnerable_time()
535 }
536
537 fn set_invulnerable_time(&self, invulnerable_time: i32) {
539 self.living_base().set_invulnerable_time(invulnerable_time);
540 }
541
542 fn set_last_hurt_by_player_memory_time(&self, time_to_remember: i32) {
544 self.living_base()
545 .set_last_hurt_by_player_memory_time(time_to_remember);
546 }
547
548 fn last_hurt_by_player_memory_time(&self) -> i32 {
550 self.living_base().last_hurt_by_player_memory_time()
551 }
552
553 fn last_hurt_by_player_uuid(&self) -> Option<Uuid> {
555 self.living_base().last_hurt_by_player_uuid()
556 }
557
558 fn last_hurt_by_mob(&self) -> Option<SharedEntity> {
560 self.living_base().last_hurt_by_mob()
561 }
562
563 fn last_hurt_by_mob_timestamp(&self) -> i32 {
565 self.living_base().last_hurt_by_mob_timestamp()
566 }
567
568 fn set_last_hurt_by_mob(&self, target: Option<&SharedEntity>) {
570 self.living_base()
571 .set_last_hurt_by_mob(target, self.tick_count());
572 }
573
574 fn last_hurt_mob(&self) -> Option<SharedEntity> {
576 self.living_base().last_hurt_mob()
577 }
578
579 fn last_hurt_mob_timestamp(&self) -> i32 {
581 self.living_base().last_hurt_mob_timestamp()
582 }
583
584 fn set_last_hurt_mob(&self, target: Option<&SharedEntity>) {
586 self.living_base()
587 .set_last_hurt_mob(target, self.tick_count());
588 }
589
590 fn resolve_mob_responsible_for_damage(&self, source: &DamageSource) {
594 if source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_ANGER) {
595 return;
596 }
597 if source.damage_type == &vanilla_damage_types::WIND_CHARGE
598 && REGISTRY.entity_types.is_in_tag(
599 self.entity_type(),
600 &EntityTypeTag::NO_ANGER_FROM_WIND_CHARGE,
601 )
602 {
603 return;
604 }
605
606 let Some(entity) = source.causing_entity() else {
607 return;
608 };
609 if entity.is_living_entity() {
610 self.set_last_hurt_by_mob(Some(entity));
611 }
612 }
613
614 fn resolve_player_responsible_for_damage(&self, source: &DamageSource) {
617 let Some(entity) = source.causing_entity() else {
618 return;
619 };
620 if entity.as_player().is_some() {
621 self.living_base()
622 .set_last_hurt_by_player_entity(entity, 100);
623 }
624 }
625
626 fn has_line_of_sight(&self, target: &dyn Entity) -> bool {
628 self.has_line_of_sight_with(
629 target,
630 ClipBlockShape::Collider,
631 ClipFluid::None,
632 target.get_eye_y(),
633 )
634 }
635
636 fn has_line_of_sight_with(
638 &self,
639 target: &dyn Entity,
640 block_shape: ClipBlockShape,
641 fluid: ClipFluid,
642 target_eye_y: f64,
643 ) -> bool {
644 let Some(world) = self.level() else {
645 return false;
646 };
647 let Some(target_world) = target.level() else {
648 return false;
649 };
650 if !Arc::ptr_eq(&world, &target_world) {
651 return false;
652 }
653
654 let position = self.position();
655 let target_position = target.position();
656 let start = DVec3::new(position.x, self.get_eye_y(), position.z);
657 let end = DVec3::new(target_position.x, target_eye_y, target_position.z);
658 if start.distance_squared(end) > 128.0 * 128.0 {
659 return false;
660 }
661
662 world.clip(start, end, block_shape, fluid).is_miss()
663 }
664
665 fn default_is_invulnerable_to(&self, source: &DamageSource) -> bool {
667 self.is_invulnerable_to_base(source)
668 }
669
670 fn is_invulnerable_to(&self, _world: &World, source: &DamageSource) -> bool {
672 self.default_is_invulnerable_to(source)
673 || enchantment_helper::is_immune_to_damage(self, source)
674 }
675
676 fn hurt_server(&self, world: &World, source: &DamageSource, amount: f32) -> bool {
681 self.default_hurt_server(world, source, amount)
682 }
683
684 fn default_hurt_server(&self, world: &World, source: &DamageSource, amount: f32) -> bool {
690 if self.is_invulnerable_to(world, source) {
691 return false;
692 }
693 if self.is_dead_or_dying() {
694 return false;
695 }
696 if source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FIRE)
697 && self.has_mob_effect(vanilla_mob_effects::FIRE_RESISTANCE)
698 {
699 return false;
700 }
701 if self.is_sleeping() {
702 self.stop_sleeping();
703 }
704
705 self.set_no_action_time(0);
706
707 let mut damage = amount;
708 if damage < 0.0 {
709 damage = 0.0;
710 }
711
712 if source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FREEZING)
714 && REGISTRY
715 .entity_types
716 .is_in_tag(self.entity_type(), &EntityTypeTag::FREEZE_HURTS_EXTRA_TYPES)
717 {
718 damage *= 5.0;
719 }
720 if !damage.is_finite() {
722 damage = f32::MAX;
723 }
724
725 let Some((took_full_damage, effective_amount)) = self
726 .living_base()
727 .apply_damage_cooldown(damage, source.bypasses_cooldown())
728 else {
729 return false;
730 };
731
732 self.before_actually_hurt(source, effective_amount);
733 self.actually_hurt(world, source, effective_amount);
734 if took_full_damage {
735 self.living_base().begin_hurt_animation();
736 }
737 self.resolve_mob_responsible_for_damage(source);
738 self.resolve_player_responsible_for_damage(source);
739
740 if took_full_damage {
741 self.broadcast_damage_event(world, source);
742 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_IMPACT) {
743 self.mark_hurt();
744 }
745 self.apply_damage_knockback(source);
746 }
747
748 if self.is_dead_or_dying() {
749 if !self.check_totem_death_protection(source) {
750 if took_full_damage {
751 self.play_death_sound();
752 }
753 self.die(source);
754 }
755 } else if took_full_damage {
756 self.play_hurt_sound(source);
757 }
758 self.record_last_damage_source(source);
761
762 true
763 }
764
765 fn before_actually_hurt(&self, _source: &DamageSource, _amount: f32) {
767 if let Some(animal) = self.as_animal() {
768 animal.reset_love();
769 }
770 }
771
772 fn hurt_armor(&self, _source: &DamageSource, _damage: f32) {}
774
775 fn do_hurt_equipment(&self, source: &DamageSource, damage: f32, slots: &[EquipmentSlot]) {
777 if damage <= 0.0 {
778 return;
779 }
780
781 let durability_damage = (damage / 4.0).max(1.0) as i32;
782 for &slot in slots {
783 let mut item_broke = false;
784 let mut item_ref = &*vanilla_items::AIR;
785 self.with_equipment_slot_mut(slot, &mut |item| {
786 item_ref = item.item;
787 let damage_on_hurt = item
788 .get_equippable()
789 .is_some_and(|equippable| equippable.damage_on_hurt);
790 if damage_on_hurt
791 && item.is_damageable_item()
792 && item.can_be_hurt_by(source.damage_type)
793 {
794 item_broke =
795 item.hurt_and_break(durability_damage, self.has_infinite_materials());
796 }
797 });
798 if item_broke {
799 self.on_equipped_item_broken(item_ref, slot);
800 }
801 }
802 }
803
804 fn get_damage_after_armor_absorb(&self, source: &DamageSource, mut damage: f32) -> f32 {
806 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_ARMOR) {
807 self.hurt_armor(source, damage);
808 let armor_toughness =
809 self.attributes()
810 .lock()
811 .required_value(vanilla_attributes::ARMOR_TOUGHNESS) as f32;
812 damage = combat_rules::get_damage_after_absorb(
813 self,
814 damage,
815 source,
816 self.get_armor_value() as f32,
817 armor_toughness,
818 );
819 }
820 damage
821 }
822
823 fn get_damage_after_magic_absorb(&self, source: &DamageSource, mut damage: f32) -> f32 {
825 if source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_EFFECTS) {
826 return damage;
827 }
828
829 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_RESISTANCE)
830 && let Some(resistance) = self.mob_effect(vanilla_mob_effects::RESISTANCE)
831 {
832 let absorb_value = (resistance.amplifier() + 1) * 5;
833 let absorb = 25 - absorb_value;
834 let old_damage = damage;
835 damage = (damage * absorb as f32 / 25.0).max(0.0);
836 let damage_resisted = old_damage - damage;
837 if (0.0..f32::MAX).contains(&damage_resisted) {
838 let stats_to_award = (damage_resisted * 10.0).round() as i32;
839 if let Some(player) = self.as_player() {
840 player.award_custom_stat_with_count(
841 &vanilla_custom_stats::DAMAGE_RESISTED,
842 stats_to_award,
843 );
844 } else if let Some(damage_causer) = source.causing_entity()
845 && let Some(player) = damage_causer.as_player()
846 {
847 player.award_custom_stat_with_count(
848 &vanilla_custom_stats::DAMAGE_DEALT_RESISTED,
849 stats_to_award,
850 );
851 }
852 }
853 }
854
855 if damage <= 0.0 {
856 return 0.0;
857 }
858 if source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_ENCHANTMENTS) {
859 return damage;
860 }
861
862 let enchantment_armor = enchantment_helper::get_damage_protection(self, source);
863 if enchantment_armor > 0.0 {
864 damage = combat_rules::get_damage_after_magic_absorb(damage, enchantment_armor);
865 }
866 damage
867 }
868
869 fn actually_hurt(&self, world: &World, source: &DamageSource, amount: f32) {
871 if self.is_invulnerable_to(world, source) {
872 return;
873 }
874
875 let damage = self.get_damage_after_armor_absorb(source, amount);
876 let damage = self.get_damage_after_magic_absorb(source, damage);
877 let original_damage = damage;
878 let damage = (damage - self.get_absorption_amount()).max(0.0);
879 self.set_absorption_amount(self.get_absorption_amount() - (original_damage - damage));
880
881 let absorbed_damage = original_damage - damage;
882 if (0.0..f32::MAX).contains(&absorbed_damage)
883 && let Some(damage_causer) = source.causing_entity()
884 && let Some(player) = damage_causer.as_player()
885 {
886 player.award_custom_stat_with_count(
887 &vanilla_custom_stats::DAMAGE_DEALT_ABSORBED,
888 (absorbed_damage * 10.0).round() as i32,
889 );
890 }
891
892 if damage != 0.0 {
893 self.set_health(self.get_health() - damage);
894 self.set_absorption_amount(self.get_absorption_amount() - damage);
895 self.game_event(&vanilla_game_events::ENTITY_DAMAGE);
896 }
897 }
898
899 fn apply_damage_knockback(&self, source: &DamageSource) {
901 if source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_KNOCKBACK) {
902 return;
903 }
904
905 let (xd, zd) = self.damage_knockback_direction(source);
906 self.knockback(DAMAGE_KNOCKBACK_POWER, xd, zd);
907 self.indicate_damage(xd, zd);
908 }
909
910 fn damage_knockback_direction(&self, source: &DamageSource) -> (f64, f64) {
912 if let Some(direct_entity) = source.direct_entity()
913 && let Some(projectile) = direct_entity.as_projectile()
914 && let Some(hurt_entity) = self.as_living_entity()
915 {
916 let (xd, zd) =
917 projectile.calculate_horizontal_hurt_knockback_direction(hurt_entity, source);
918 return (-xd, -zd);
919 }
920
921 let Some(source_position) = source.source_position() else {
922 return (0.0, 0.0);
923 };
924
925 let position = self.position();
926 (
927 source_position.x - position.x,
928 source_position.z - position.z,
929 )
930 }
931
932 fn knockback(&self, mut power: f64, mut xd: f64, mut zd: f64) {
935 power *= 1.0 - self.knockback_resistance();
936 if power <= 0.0 {
937 return;
938 }
939
940 while xd * xd + zd * zd < KNOCKBACK_DIRECTION_EPSILON_SQ {
941 xd = (rand::random::<f64>() - rand::random::<f64>()) * 0.01;
942 zd = (rand::random::<f64>() - rand::random::<f64>()) * 0.01;
943 }
944
945 let old_velocity = self.velocity();
946 let delta_vector = DVec3::new(xd, 0.0, zd).normalize() * power;
947 self.set_velocity(DVec3::new(
948 old_velocity.x / 2.0 - delta_vector.x,
949 if self.on_ground() {
950 0.4_f64.min(old_velocity.y / 2.0 + power)
951 } else {
952 old_velocity.y
953 },
954 old_velocity.z / 2.0 - delta_vector.z,
955 ));
956 self.mark_velocity_sync();
957 }
958
959 fn knockback_resistance(&self) -> f64 {
961 self.attributes()
962 .lock()
963 .required_value(vanilla_attributes::KNOCKBACK_RESISTANCE)
964 }
965
966 fn indicate_damage(&self, _xd: f64, _zd: f64) {}
968
969 fn hurt_broadcast_chunk(&self) -> ChunkPos {
971 ChunkPos::from_entity_pos(self.position())
972 }
973
974 fn broadcast_damage_event(&self, world: &World, source: &DamageSource) {
976 world.broadcast_to_nearby(
977 self.hurt_broadcast_chunk(),
978 CDamageEvent {
979 entity_id: self.id(),
980 source_type_id: source.damage_type.id() as i32,
981 source_cause_id: source
982 .causing_entity()
983 .map_or(0, |entity| entity.id().wrapping_add(1)),
984 source_direct_id: source
985 .direct_entity()
986 .map_or(0, |entity| entity.id().wrapping_add(1)),
987 source_position: source.source_position_raw(),
988 },
989 None,
990 );
991 }
992
993 fn check_totem_death_protection(&self, source: &DamageSource) -> bool {
995 if source.bypasses_invulnerability() {
996 return false;
997 }
998
999 let mut used = None;
1000 for slot in [EquipmentSlot::MainHand, EquipmentSlot::OffHand] {
1001 self.with_equipment_slot_mut(slot, &mut |stack| {
1002 if let Some(protection) = stack.get(DEATH_PROTECTION).cloned() {
1003 used = Some((protection, stack.clone()));
1004 stack.shrink(1);
1005 }
1006 });
1007 if used.is_some() {
1008 break;
1009 }
1010 }
1011 let Some((protection, protection_item)) = used else {
1012 return false;
1013 };
1014
1015 if let Some(player) = self.as_player() {
1016 player.award_stat(&vanilla_stat_types::ITEM_USED, protection_item.item());
1017 if protection_item
1019 .get(USE_EFFECTS)
1020 .is_some_and(|effects| effects.interact_vibrations)
1021 {
1022 self.game_event(&vanilla_game_events::ITEM_INTERACT_FINISH);
1023 }
1024 }
1025
1026 self.set_health(1.0);
1027 if let (Some(world), Some(entity)) = (self.level(), self.as_living_entity()) {
1028 for effect in protection.death_effects() {
1029 apply_consume_effect(effect, &world, entity);
1030 }
1031 }
1032 self.broadcast_entity_event(EntityStatus::ProtectedFromDeath);
1033 true
1034 }
1035
1036 fn die(&self, source: &DamageSource) {
1038 if self.is_removed() {
1039 return;
1040 }
1041 if !self.living_base().mark_death_processed() {
1042 return;
1043 }
1044
1045 self.handle_killing_blow();
1046
1047 if let Some(world) = self.level()
1050 && let Some(self_entity) = self.as_living_entity()
1051 {
1052 let source_entity = source.causing_entity();
1053 if source_entity.is_none_or(|entity| entity.killed_entity(&world, self_entity, source))
1054 {
1055 self.game_event(&vanilla_game_events::ENTITY_DIE);
1056 self.drop_all_death_loot(source);
1057 }
1059 }
1060
1061 self.broadcast_entity_event(EntityStatus::Death);
1062 self.set_pose(EntityPose::Dying);
1063 }
1064
1065 fn handle_killing_blow(&self) {}
1072
1073 fn should_drop_loot(&self, world: &World) -> bool {
1077 !self.is_baby() && world.get_game_rule(&MOB_DROPS)
1078 }
1079
1080 fn should_drop_experience(&self) -> bool {
1082 !self.is_baby()
1083 }
1084
1085 fn is_always_experience_dropper(&self) -> bool {
1088 false
1089 }
1090
1091 fn skip_drop_experience(&self) {
1093 self.living_base().skip_drop_experience();
1094 }
1095
1096 fn was_experience_consumed(&self) -> bool {
1098 self.living_base().was_experience_consumed()
1099 }
1100
1101 fn base_experience_reward(&self) -> i32 {
1104 if let Some(animal) = self.as_animal() {
1105 return animal.base_experience_reward_animal();
1106 }
1107
1108 self.as_mob().map_or(0, Mob::base_experience_reward_mob)
1109 }
1110
1111 fn experience_reward(&self, _world: &World, _killer_entity: Option<&dyn Entity>) -> i32 {
1114 self.base_experience_reward()
1117 }
1118
1119 fn drop_all_death_loot(&self, source: &DamageSource) {
1121 let Some(world) = self.level() else {
1122 return;
1123 };
1124 if self.should_drop_loot(world.as_ref()) {
1125 let killed_by_player = self.last_hurt_by_player_memory_time() > 0;
1126 self.drop_from_loot_table(source, killed_by_player);
1127 self.drop_custom_death_loot(source, killed_by_player);
1128 if let Some(mob) = self.as_mob() {
1129 mob.drop_custom_death_loot_mob(source, killed_by_player);
1130 }
1131 }
1132 self.drop_experience(&world, source.causing_entity().map(AsRef::as_ref));
1133 }
1135
1136 fn drop_experience(&self, world: &Arc<World>, killer_entity: Option<&dyn Entity>) {
1138 if self.was_experience_consumed() {
1139 return;
1140 }
1141
1142 let should_drop = self.is_always_experience_dropper()
1143 || self.last_hurt_by_player_memory_time() > 0
1144 && self.should_drop_experience()
1145 && world.get_game_rule(&MOB_DROPS);
1146 if !should_drop {
1147 return;
1148 }
1149
1150 let reward = self.experience_reward(world, killer_entity);
1151 if reward > 0 {
1152 ExperienceOrbEntity::award(world, self.position(), reward);
1153 }
1154 }
1155
1156 fn death_loot_table(&self) -> Option<LootTableRef> {
1159 if let Some(mob) = self.as_mob()
1160 && mob.has_custom_death_loot_table()
1161 {
1162 return mob.custom_death_loot_table();
1163 }
1164
1165 let entity_type = self.entity_type();
1166 let loot_key = Identifier::vanilla(format!("entities/{}", entity_type.key.path));
1167 REGISTRY.loot_tables.by_key(&loot_key)
1168 }
1169
1170 fn death_loot_table_seed(&self) -> i64 {
1172 self.as_mob().map_or(0, Mob::death_loot_table_seed)
1173 }
1174
1175 fn drop_from_loot_table(&self, source: &DamageSource, killed_by_player: bool) {
1178 let Some(world) = self.level() else {
1179 return;
1180 };
1181 let has_custom_death_loot_table =
1182 self.as_mob().is_some_and(Mob::has_custom_death_loot_table);
1183 let Some(loot_table) = self.death_loot_table() else {
1184 if has_custom_death_loot_table && let Some(mob) = self.as_mob() {
1185 mob.clear_custom_death_loot_table();
1186 }
1187 return;
1188 };
1189
1190 let seed = self.death_loot_table_seed();
1191 let drops = if seed == 0 {
1192 let mut rng = rand::rng();
1193 death_loot_items_with_rng(
1194 self,
1195 loot_table,
1196 world.as_ref(),
1197 source,
1198 killed_by_player,
1199 &mut rng,
1200 )
1201 } else {
1202 let mut rng = StdRng::seed_from_u64(seed as u64);
1203 death_loot_items_with_rng(
1204 self,
1205 loot_table,
1206 world.as_ref(),
1207 source,
1208 killed_by_player,
1209 &mut rng,
1210 )
1211 };
1212
1213 if has_custom_death_loot_table && let Some(mob) = self.as_mob() {
1214 mob.clear_custom_death_loot_table();
1215 }
1216
1217 for item_stack in drops {
1218 self.spawn_at_location(item_stack, 0.0);
1219 }
1220 }
1221
1222 fn drop_custom_death_loot(&self, _source: &DamageSource, _killed_by_player: bool) {}
1224
1225 fn tick_death(&self) {
1227 let death_time = self.living_base().increment_death_time();
1228 if death_time >= DEATH_DURATION && !self.is_removed() {
1229 self.broadcast_entity_event(EntityStatus::Poof);
1230 self.set_removed(RemovalReason::Killed);
1231 }
1232 }
1233
1234 fn get_absorption_amount(&self) -> f32 {
1236 self.living_base().absorption_amount()
1237 }
1238
1239 fn set_absorption_amount(&self, amount: f32) {
1241 self.living_base().set_absorption_amount(amount);
1242 }
1243
1244 fn fall_damage_sound(&self, damage: i32) -> SoundEventRef {
1246 let (small, big) = self.fall_sounds();
1247 if damage > 4 { big } else { small }
1248 }
1249
1250 fn play_block_fall_sound(&self) {
1252 let Some(world) = self.level() else {
1253 return;
1254 };
1255 let position = self.position();
1256 let pos = BlockPos::new(
1257 position.x.floor() as i32,
1258 (position.y - f64::from(0.2_f32)).floor() as i32,
1259 position.z.floor() as i32,
1260 );
1261 let state = world.get_block_state(pos);
1262 if state.is_air() {
1263 return;
1264 }
1265
1266 let sound_type = state.get_block().config.sound_type;
1267 self.play_sound(
1268 sound_type.fall_sound,
1269 sound_type.volume * 0.5,
1270 sound_type.pitch * 0.75,
1271 );
1272 }
1273
1274 fn cause_living_fall_damage(
1276 &self,
1277 fall_distance: f64,
1278 damage_modifier: f32,
1279 source: &DamageSource,
1280 ) -> bool {
1281 let effective_fall_distance =
1282 if let Some(impact_pos) = self.living_base().current_impulse_impact_pos() {
1283 let effective_fall_distance = fall_distance.min(impact_pos.y - self.position().y);
1284 if effective_fall_distance <= 0.0 {
1285 self.reset_current_impulse_context();
1286 } else {
1287 self.try_reset_current_impulse_context();
1288 }
1289 effective_fall_distance
1290 } else {
1291 fall_distance
1292 };
1293
1294 if self.is_fall_damage_immune() {
1295 return false;
1296 }
1297
1298 self.propagate_fall_to_passengers(effective_fall_distance, damage_modifier, source);
1299
1300 let attributes = self.attributes().lock();
1301 let safe_fall_distance = attributes
1302 .get_value(vanilla_attributes::SAFE_FALL_DISTANCE)
1303 .unwrap_or(vanilla_attributes::SAFE_FALL_DISTANCE.default_value);
1304 let fall_damage_multiplier = attributes
1305 .get_value(vanilla_attributes::FALL_DAMAGE_MULTIPLIER)
1306 .unwrap_or(vanilla_attributes::FALL_DAMAGE_MULTIPLIER.default_value);
1307 drop(attributes);
1308
1309 let damage = LivingEntityBase::calculate_fall_damage(
1310 effective_fall_distance,
1311 damage_modifier,
1312 safe_fall_distance,
1313 fall_damage_multiplier,
1314 );
1315 if damage <= 0 {
1316 return false;
1317 }
1318
1319 self.reset_current_impulse_context();
1320 self.play_sound(self.fall_damage_sound(damage), 1.0, 1.0);
1321 self.play_block_fall_sound();
1322 if let Some(world) = self.level() {
1323 self.hurt(&world, source, damage as f32);
1324 }
1325 true
1326 }
1327
1328 fn get_armor_value(&self) -> i32 {
1330 self.attributes()
1331 .lock()
1332 .get_value(vanilla_attributes::ARMOR)
1333 .unwrap_or(0.0) as i32
1334 }
1335
1336 fn get_attribute_gravity(&self) -> f64 {
1338 self.attributes()
1339 .lock()
1340 .required_value(vanilla_attributes::GRAVITY)
1341 }
1342
1343 fn get_effective_gravity(&self) -> f64 {
1345 let gravity = self.get_gravity();
1346 if self.velocity().y <= 0.0 && self.has_mob_effect(vanilla_mob_effects::SLOW_FALLING) {
1347 gravity.min(0.01)
1348 } else {
1349 gravity
1350 }
1351 }
1352
1353 fn is_affected_by_potions(&self) -> bool {
1355 !self.is_dead_or_dying()
1356 }
1357
1358 fn is_inverted_heal_and_harm(&self) -> bool {
1360 REGISTRY.entity_types.is_in_tag(
1361 self.entity_type(),
1362 &EntityTypeTag::INVERTED_HEALING_AND_HARM,
1363 )
1364 }
1365
1366 fn default_can_be_affected(&self, effect: &MobEffectInstance) -> bool {
1368 if REGISTRY
1369 .entity_types
1370 .is_in_tag(self.entity_type(), &EntityTypeTag::IMMUNE_TO_INFESTED)
1371 {
1372 return effect.effect() != vanilla_mob_effects::INFESTED;
1373 }
1374 if REGISTRY
1375 .entity_types
1376 .is_in_tag(self.entity_type(), &EntityTypeTag::IMMUNE_TO_OOZING)
1377 {
1378 return effect.effect() != vanilla_mob_effects::OOZING;
1379 }
1380 if REGISTRY
1381 .entity_types
1382 .is_in_tag(self.entity_type(), &EntityTypeTag::IGNORES_POISON_AND_REGEN)
1383 {
1384 return effect.effect() != vanilla_mob_effects::REGENERATION
1385 && effect.effect() != vanilla_mob_effects::POISON;
1386 }
1387
1388 true
1389 }
1390
1391 fn can_be_affected(&self, effect: &MobEffectInstance) -> bool {
1395 self.default_can_be_affected(effect)
1396 }
1397
1398 fn has_mob_effect(&self, effect: MobEffectRef) -> bool {
1400 self.living_base().has_mob_effect(effect)
1401 }
1402
1403 fn mob_effect(&self, effect: MobEffectRef) -> Option<ActiveMobEffect> {
1405 self.living_base().mob_effect(effect)
1406 }
1407
1408 fn active_mob_effects(&self) -> Vec<ActiveMobEffect> {
1410 self.living_base().active_mob_effects()
1411 }
1412
1413 fn set_mob_effect(&self, effect: MobEffectRef, amplifier: i32) {
1415 self.add_mob_effect(MobEffectInstance::new(effect, amplifier));
1416 }
1417
1418 fn add_mob_effect(&self, effect: MobEffectInstance) -> bool {
1420 self.add_mob_effect_with_source(effect, None)
1421 }
1422
1423 fn add_mob_effect_with_source(
1426 &self,
1427 effect: MobEffectInstance,
1428 source: Option<&dyn Entity>,
1429 ) -> bool {
1430 if !self.can_be_affected(&effect) {
1431 return false;
1432 }
1433 let (effect_key, amplifier) = (effect.effect(), effect.amplifier());
1434 let changed = self.living_base().add_mob_effect(effect);
1435 let _ = source;
1438 let dyn_self = self
1440 .as_living_entity()
1441 .expect("Self implements LivingEntity");
1442 MOB_EFFECT_BEHAVIORS
1443 .get_behavior(effect_key)
1444 .on_effect_started(dyn_self, amplifier);
1445 changed
1446 }
1447
1448 fn set_mob_effect_active(&self, effect: MobEffectRef, active: bool) {
1450 if active {
1451 self.set_mob_effect(effect, 0);
1452 } else {
1453 self.remove_mob_effect(effect);
1454 }
1455 }
1456
1457 fn remove_mob_effect(&self, effect: MobEffectRef) -> bool {
1459 self.living_base().remove_mob_effect(effect)
1460 }
1461
1462 fn tick_mob_effects(&self) {
1464 let world = self.level();
1465 let dyn_self = self
1466 .as_living_entity()
1467 .expect("Self implements LivingEntity");
1468 for effect in self.active_mob_effects() {
1469 if !effect.has_remaining_duration() {
1470 self.living_base().tick_mob_effect_duration(effect.effect());
1471 continue;
1472 }
1473
1474 if effect.should_apply_effect_tick_this_tick(self.tick_count())
1475 && world
1476 .as_deref()
1477 .is_some_and(|world| !effect.apply_effect_tick(world, dyn_self))
1478 {
1479 self.remove_mob_effect(effect.effect());
1480 continue;
1481 }
1482
1483 self.living_base().tick_mob_effect_duration(effect.effect());
1484 }
1485 }
1486
1487 fn has_water_breathing(&self) -> bool {
1489 self.has_mob_effect(vanilla_mob_effects::WATER_BREATHING)
1490 || self.has_mob_effect(vanilla_mob_effects::CONDUIT_POWER)
1491 || self.has_mob_effect(vanilla_mob_effects::BREATH_OF_THE_NAUTILUS)
1492 }
1493
1494 fn should_effects_refill_air_supply(&self) -> bool {
1496 !self.has_mob_effect(vanilla_mob_effects::BREATH_OF_THE_NAUTILUS)
1497 || self.has_mob_effect(vanilla_mob_effects::WATER_BREATHING)
1498 || self.has_mob_effect(vanilla_mob_effects::CONDUIT_POWER)
1499 }
1500
1501 fn can_breathe_underwater(&self) -> bool {
1503 self.entity_type().flags.can_breathe_underwater
1504 }
1505
1506 fn can_drown_in_water(&self) -> bool {
1508 if self.can_breathe_underwater() || self.has_water_breathing() {
1509 return false;
1510 }
1511
1512 !self
1513 .as_player()
1514 .is_some_and(|player| player.abilities.lock().invulnerable)
1515 }
1516
1517 fn is_eye_in_bubble_column(&self) -> bool {
1519 let Some(world) = self.level() else {
1520 return false;
1521 };
1522
1523 world
1524 .get_block_state(BlockPos::new(
1525 self.position().x.floor() as i32,
1526 self.get_eye_y().floor() as i32,
1527 self.position().z.floor() as i32,
1528 ))
1529 .get_block()
1530 == &vanilla_blocks::BUBBLE_COLUMN
1531 }
1532
1533 fn decrease_air_supply(&self, current_supply: i32) -> i32 {
1535 let oxygen_bonus = self
1536 .attributes()
1537 .lock()
1538 .get_value(vanilla_attributes::OXYGEN_BONUS)
1539 .unwrap_or(0.0);
1540 if oxygen_bonus > 0.0 && rand::random::<f64>() >= 1.0 / (oxygen_bonus + 1.0) {
1541 current_supply
1542 } else {
1543 current_supply - 1
1544 }
1545 }
1546
1547 fn increase_air_supply(&self, current_supply: i32) -> i32 {
1549 (current_supply + 4).min(self.max_air_supply())
1550 }
1551
1552 fn should_take_drowning_damage(&self) -> bool {
1554 self.air_supply() <= -20
1555 }
1556
1557 fn tick_living_air_supply(&self) {
1559 if !LivingEntity::is_alive(self) {
1560 return;
1561 }
1562
1563 let eye_in_water = self.is_eye_in_water() && !self.is_eye_in_bubble_column();
1564 if eye_in_water {
1565 if self.can_drown_in_water() {
1566 self.set_air_supply(self.decrease_air_supply(self.air_supply()));
1567 if self.should_take_drowning_damage() {
1568 self.set_air_supply(0);
1569 self.broadcast_entity_event(EntityStatus::DrownParticles);
1570 if let Some(world) = self.level() {
1571 self.hurt(
1572 &world,
1573 &DamageSource::environment(&vanilla_damage_types::DROWN),
1574 2.0,
1575 );
1576 }
1577 }
1578 } else if self.air_supply() < self.max_air_supply()
1579 && self.should_effects_refill_air_supply()
1580 {
1581 self.set_air_supply(self.increase_air_supply(self.air_supply()));
1582 }
1583
1584 if self
1585 .vehicle()
1586 .is_some_and(|vehicle| vehicle.dismounts_underwater())
1587 {
1588 self.stop_riding();
1589 }
1590 return;
1591 }
1592
1593 if self.air_supply() < self.max_air_supply() {
1594 self.set_air_supply(self.increase_air_supply(self.air_supply()));
1595 }
1596 }
1597
1598 fn is_in_wall(&self) -> bool {
1600 !self.is_sleeping() && Entity::is_in_wall(self)
1601 }
1602
1603 fn tick_in_wall_damage(&self) {
1605 if !LivingEntity::is_alive(self) || !LivingEntity::is_in_wall(self) {
1606 return;
1607 }
1608
1609 if let Some(world) = self.level() {
1610 self.hurt(
1611 &world,
1612 &DamageSource::environment(&vanilla_damage_types::IN_WALL),
1613 1.0,
1614 );
1615 }
1616 }
1617
1618 fn tick_living_environmental_damage(&self) {
1620 if !LivingEntity::is_alive(self) {
1621 return;
1622 }
1623
1624 if LivingEntity::is_in_wall(self) {
1625 self.tick_in_wall_damage();
1626 } else if self.as_player().is_some()
1627 && let Some(world) = self.level()
1628 {
1629 let border = world.world_border_snapshot();
1630 let position = self.position();
1631 if let Some(damage) =
1632 border.outside_damage_amount(position.x, position.z, self.bounding_box())
1633 {
1634 self.hurt(
1635 &world,
1636 &DamageSource::environment(&vanilla_damage_types::OUTSIDE_BORDER),
1637 damage,
1638 );
1639 }
1640 }
1641
1642 self.tick_living_air_supply();
1643 }
1644
1645 fn is_affected_by_fluids(&self) -> bool {
1647 true
1648 }
1649
1650 fn can_stand_on_fluid(&self, _fluid_state: FluidState) -> bool {
1652 false
1653 }
1654
1655 fn is_using_item(&self) -> bool {
1657 false
1658 }
1659
1660 fn is_blocking(&self) -> bool {
1662 false
1663 }
1664
1665 fn is_fall_flying(&self) -> bool {
1667 self.living_base().is_fall_flying()
1668 }
1669
1670 fn set_fall_flying(&self, fall_flying: bool) {
1672 self.set_shared_fall_flying(fall_flying);
1673 self.living_base().set_fall_flying(fall_flying);
1674 }
1675
1676 fn fall_flying_ticks(&self) -> i32 {
1678 self.living_base().fall_flying_ticks()
1679 }
1680
1681 fn with_equipment_slot(&self, slot: EquipmentSlot, visitor: &mut dyn FnMut(&ItemStack)) {
1683 let equipment = self.living_base().equipment().lock();
1684 visitor(equipment.get_ref(slot));
1685 }
1686
1687 fn is_holding(&self, predicate: &mut dyn FnMut(&ItemStack) -> bool) -> bool {
1689 let mut holding = false;
1690 self.with_equipment_slot(EquipmentSlot::MainHand, &mut |item_stack| {
1691 holding = predicate(item_stack);
1692 });
1693 if holding {
1694 return true;
1695 }
1696
1697 self.with_equipment_slot(EquipmentSlot::OffHand, &mut |item_stack| {
1698 holding = predicate(item_stack);
1699 });
1700 holding
1701 }
1702
1703 fn tick_equipment(&self) {
1705 let (Some(world), Some(owner)) = (self.level(), self.as_living_entity()) else {
1706 return;
1707 };
1708 InventoryTickContext::tick_equipment(&world, owner, EquipmentSlot::ALL);
1709 }
1710
1711 fn with_equipment_slot_mut(
1713 &self,
1714 slot: EquipmentSlot,
1715 visitor: &mut dyn FnMut(&mut ItemStack),
1716 ) {
1717 let mut equipment = self.living_base().equipment().lock();
1718 visitor(equipment.get_mut(slot));
1719 }
1720
1721 fn has_item_in_slot(&self, slot: EquipmentSlot) -> bool {
1723 let mut has_item = false;
1724 self.with_equipment_slot(slot, &mut |item_stack| {
1725 has_item = !item_stack.is_empty();
1726 });
1727 has_item
1728 }
1729
1730 fn can_use_slot(&self, _slot: EquipmentSlot) -> bool {
1732 true
1733 }
1734
1735 fn can_dispenser_equip_into_slot(&self, _slot: EquipmentSlot) -> bool {
1737 self.as_mob().is_none_or(Mob::can_pick_up_loot)
1738 }
1739
1740 fn can_equip_with_dispenser(&self, item_stack: &ItemStack) -> bool {
1744 if !Entity::is_alive(self) || self.is_spectator() {
1745 return false;
1746 }
1747
1748 let Some(equippable) = item_stack.get_equippable() else {
1749 return false;
1750 };
1751 if !equippable.dispensable {
1752 return false;
1753 }
1754
1755 let slot = equippable.slot;
1756 self.can_use_slot(slot)
1757 && equippable.can_be_equipped_by(self.entity_type())
1758 && !self.has_item_in_slot(slot)
1759 && self.can_dispenser_equip_into_slot(slot)
1760 }
1761
1762 fn is_equippable_in_slot(&self, item_stack: &ItemStack, slot: EquipmentSlot) -> bool {
1765 let Some(equippable) = item_stack.get_equippable() else {
1766 return slot == EquipmentSlot::MainHand && self.can_use_slot(EquipmentSlot::MainHand);
1767 };
1768
1769 slot == equippable.slot
1770 && self.can_use_slot(equippable.slot)
1771 && equippable.can_be_equipped_by(self.entity_type())
1772 }
1773
1774 fn equip_sound(&self, slot: EquipmentSlot, stack: &ItemStack) -> Option<SoundEventRef> {
1776 let equippable = stack.get_equippable()?;
1777 (slot == equippable.slot)
1778 .then(|| equippable.equip_sound.registry_ref())
1779 .flatten()
1780 }
1781
1782 fn on_equip_item(&self, slot: EquipmentSlot, previous: &ItemStack, stack: &ItemStack) {
1784 if self.is_spectator()
1785 || self.is_first_tick()
1786 || ItemStack::is_same_item_same_components(previous, stack)
1787 {
1788 return;
1789 }
1790
1791 let Some(sound) = self.equip_sound(slot, stack) else {
1792 return;
1793 };
1794 if self.is_silent() {
1795 return;
1796 }
1797 if let Some(world) = self.level() {
1798 world.play_sound_at(sound, self.sound_source(), self.position(), 1.0, 1.0, None);
1799 }
1800 }
1801
1802 fn interact_living_entity_with_equippable(
1804 &self,
1805 player: &Player,
1806 hand: InteractionHand,
1807 ) -> InteractionResult {
1808 let item_stack = {
1809 let inventory = player.inventory.lock();
1810 let item_stack = inventory.get_item_in_hand(hand);
1811 item_stack.copy_with_count(item_stack.count())
1812 };
1813 let Some(equippable) = item_stack.get_equippable() else {
1814 return InteractionResult::Pass;
1815 };
1816 if !equippable.equip_on_interact {
1817 return InteractionResult::Pass;
1818 }
1819
1820 let slot = equippable.slot;
1821 if !self.is_equippable_in_slot(&item_stack, slot) || !Entity::is_alive(self) {
1822 return InteractionResult::Pass;
1823 }
1824
1825 let equipped = {
1826 let mut equipment = self.living_base().equipment().lock();
1827 if !equipment.get_ref(slot).is_empty() {
1828 return InteractionResult::Pass;
1829 }
1830
1831 let mut inventory = player.inventory.lock();
1832 if !self.is_equippable_in_slot(inventory.get_item_in_hand(hand), slot) {
1833 return InteractionResult::Pass;
1834 }
1835
1836 let equipped = inventory.split_item_in_hand(hand, 1);
1837 if equipped.is_empty() {
1838 return InteractionResult::Pass;
1839 }
1840
1841 equipment.set(slot, equipped);
1842 equipment.get_ref(slot).copy_with_count(1)
1843 };
1844
1845 self.on_equip_item(slot, &ItemStack::empty(), &equipped);
1846 if let Some(mob) = self.as_mob() {
1847 mob.set_guaranteed_drop(slot);
1848 }
1849 InteractionResult::Success
1851 }
1852
1853 fn refresh_equipment_attribute_modifiers(&self, slot: EquipmentSlot) {
1855 self.with_equipment_slot(slot, &mut |item_stack| {
1856 self.living_base()
1857 .refresh_equipment_attribute_modifiers(slot, item_stack);
1858 });
1859 }
1860
1861 fn detect_equipment_updates(&self) {
1863 let mut changes = self.living_base().collect_equipment_changes();
1864 if changes.is_empty() {
1865 return;
1866 }
1867
1868 for (slot, _, current) in &changes {
1869 self.living_base()
1870 .refresh_equipment_attribute_modifiers(*slot, current);
1871 }
1872
1873 let main_hand = changes
1874 .iter()
1875 .find(|(slot, _, _)| *slot == EquipmentSlot::MainHand);
1876 let offhand = changes
1877 .iter()
1878 .find(|(slot, _, _)| *slot == EquipmentSlot::OffHand);
1879 let hands_swapped = main_hand.zip(offhand).is_some_and(
1880 |((_, previous_main, current_main), (_, previous_off, current_off))| {
1881 ItemStack::matches(current_main, previous_off)
1882 && ItemStack::matches(current_off, previous_main)
1883 },
1884 );
1885
1886 if hands_swapped {
1887 if let Some(world) = self.level() {
1888 world.broadcast_to_entity_trackers(
1889 self.id(),
1890 CEntityEvent {
1891 entity_id: self.id(),
1892 event: EntityStatus::SwapHands,
1893 },
1894 None,
1895 );
1896 }
1897 changes.retain(|(slot, _, _)| {
1898 !matches!(slot, EquipmentSlot::MainHand | EquipmentSlot::OffHand)
1899 });
1900 }
1901
1902 self.living_base().queue_equipment_changes(
1903 changes
1904 .into_iter()
1905 .map(|(slot, _, current)| (slot, current)),
1906 );
1907 }
1908
1909 fn pack_living_equipment(&self) -> Vec<EquipmentSlotItem> {
1911 equipment_items_to_packet_items(self.living_base().equipment().lock().non_empty_items())
1912 }
1913
1914 fn drain_dirty_living_equipment(&self) -> Vec<EquipmentSlotItem> {
1916 equipment_items_to_packet_items(self.living_base().drain_equipment_changes())
1917 }
1918
1919 fn has_infinite_materials(&self) -> bool {
1921 false
1922 }
1923
1924 fn handle_extra_items_created_on_use(&self, _extra: ItemStack) {}
1927
1928 fn on_equipped_item_broken(&self, _item: ItemRef, slot: EquipmentSlot) {
1930 self.broadcast_entity_event(slot.into());
1931 self.refresh_equipment_attribute_modifiers(slot);
1932 }
1933
1934 fn default_living_can_freeze(&self) -> bool {
1940 for slot in EquipmentSlot::ALL {
1941 if !slot.is_armor() {
1942 continue;
1943 }
1944 let mut is_freeze_immune = false;
1945 self.with_equipment_slot(slot, &mut |item_stack| {
1946 is_freeze_immune = REGISTRY
1947 .items
1948 .is_in_tag(item_stack.item(), &ItemTag::FREEZE_IMMUNE_WEARABLES);
1949 });
1950
1951 if is_freeze_immune {
1952 return false;
1953 }
1954 }
1955
1956 self.default_can_freeze()
1957 }
1958
1959 fn is_on_non_air_block_for_frost(&self) -> bool {
1962 let Some(world) = self.level() else {
1963 return false;
1964 };
1965 let Some(pos) = self.on_pos_legacy() else {
1966 return false;
1967 };
1968
1969 world.get_block_state(pos).get_block() != &vanilla_blocks::AIR
1970 }
1971
1972 fn remove_frost(&self) {
1974 self.attributes().lock().remove_modifier(
1975 vanilla_attributes::MOVEMENT_SPEED,
1976 &SPEED_MODIFIER_POWDER_SNOW_ID,
1977 );
1978 }
1979
1980 fn try_add_frost(&self) {
1982 if !self.is_on_non_air_block_for_frost() || self.ticks_frozen() <= 0 {
1983 return;
1984 }
1985
1986 self.attributes().lock().add_modifier(
1987 vanilla_attributes::MOVEMENT_SPEED,
1988 AttributeModifier {
1989 id: SPEED_MODIFIER_POWDER_SNOW_ID,
1990 amount: f64::from(-0.05_f32 * self.percent_frozen()),
1991 operation: AttributeModifierOperation::AddValue,
1992 },
1993 false,
1994 );
1995 }
1996
1997 fn tick_freezing(&self) {
2001 if !self.is_in_powder_snow() || !self.can_freeze() {
2002 self.set_ticks_frozen((self.ticks_frozen() - 2).max(0));
2003 }
2004
2005 self.remove_frost();
2006 self.try_add_frost();
2007 if self.tick_count() % 40 == 0
2008 && self.is_fully_frozen()
2009 && self.can_freeze()
2010 && let Some(world) = self.level()
2011 {
2012 self.hurt(
2013 &world,
2014 &DamageSource::environment(&vanilla_damage_types::FREEZE),
2015 1.0,
2016 );
2017 }
2018 }
2019
2020 fn tick_living_entity(&self, entity: &SharedEntity) {
2024 self.default_tick();
2025 self.living_base().decrement_invulnerable_time();
2026
2027 if self.is_dead_or_dying() {
2028 self.tick_death();
2029 }
2030
2031 self.tick_mob_effects();
2032 self.detect_equipment_updates();
2033
2034 if !self.is_removed() {
2035 self.ai_step(entity);
2036 }
2037
2038 self.tick_living_state();
2039 }
2040
2041 fn tick_living_state(&self) {
2043 if let Some(mob) = self.as_mob() {
2044 mob.tick_body_rotation_control();
2045 }
2046 self.living_base()
2047 .tick_fall_flying_state(self.is_fall_flying());
2048 self.refresh_dirty_attributes();
2049 self.living_base().tick_post_impulse_grace_time();
2050 }
2051
2052 fn can_glide_using(&self, item_stack: &ItemStack, slot: EquipmentSlot) -> bool {
2054 let Some(equippable) = item_stack.get_equippable() else {
2055 return false;
2056 };
2057
2058 item_stack.has(GLIDER) && equippable.slot == slot && !item_stack.next_damage_will_break()
2059 }
2060
2061 fn can_glide_using_equipment_slot(&self, slot: EquipmentSlot) -> bool {
2063 let mut can_glide = false;
2064 self.with_equipment_slot(slot, &mut |item_stack| {
2065 can_glide = self.can_glide_using(item_stack, slot);
2066 });
2067 can_glide
2068 }
2069
2070 fn damage_random_glider(&self) {
2073 let mut slots_with_gliders = Vec::new();
2074 for slot in EquipmentSlot::ALL {
2075 if self.can_glide_using_equipment_slot(slot) {
2076 slots_with_gliders.push(slot);
2077 }
2078 }
2079
2080 let slot_count = slots_with_gliders.len();
2081 if slot_count == 0 {
2082 return;
2083 }
2084
2085 let slot_index = rand::random_range(0..slot_count);
2086 let slot_to_damage = slots_with_gliders[slot_index];
2087 let has_infinite_materials = self.has_infinite_materials();
2088 let mut item_broke = false;
2089 let mut item_ref = &*vanilla_items::AIR;
2090 self.with_equipment_slot_mut(slot_to_damage, &mut |item_stack| {
2091 item_ref = item_stack.item;
2092 item_broke = item_stack.hurt_and_break(1, has_infinite_materials);
2093 });
2094 if item_broke {
2095 self.on_equipped_item_broken(item_ref, slot_to_damage);
2096 }
2097 }
2098
2099 fn default_can_glide(&self) -> bool {
2103 !self.on_ground()
2104 && !self.is_passenger()
2105 && !self.has_mob_effect(vanilla_mob_effects::LEVITATION)
2106 && EquipmentSlot::ALL
2107 .iter()
2108 .any(|&slot| self.can_glide_using_equipment_slot(slot))
2109 }
2110
2111 fn can_glide(&self) -> bool {
2113 self.default_can_glide()
2114 }
2115
2116 fn start_fall_flying(&self) {
2118 self.set_fall_flying(true);
2119 }
2120
2121 fn try_to_start_fall_flying(&self) -> bool {
2123 if !self.is_fall_flying() && self.can_glide() && !self.is_in_water() {
2124 self.start_fall_flying();
2125 return true;
2126 }
2127
2128 false
2129 }
2130
2131 fn last_climbable_pos(&self) -> Option<BlockPos> {
2133 self.living_base().last_climbable_pos()
2134 }
2135
2136 fn set_last_climbable_pos(&self, pos: BlockPos) {
2138 self.living_base().set_last_climbable_pos(pos);
2139 }
2140
2141 fn default_living_on_climbable(&self) -> bool {
2144 if self.is_spectator() {
2145 return false;
2146 }
2147
2148 let pos = self.block_position();
2149 let Some(world) = self.level() else {
2150 return false;
2151 };
2152 let state = world.get_block_state(pos);
2153 let block = state.get_block();
2154
2155 if self.is_fall_flying() && block.has_tag(&BlockTag::CAN_GLIDE_THROUGH) {
2156 return false;
2157 }
2158
2159 let climbable = block.has_tag(&BlockTag::CLIMBABLE)
2160 || block.has_tag(&BlockTag::TRAPDOORS)
2161 && trapdoor_usable_as_ladder_state(state, world.get_block_state(pos.below()));
2162
2163 if climbable {
2164 self.set_last_climbable_pos(pos);
2165 }
2166
2167 climbable
2168 }
2169
2170 fn should_discard_friction(&self) -> bool {
2172 self.living_base().should_discard_friction()
2173 }
2174
2175 fn set_discard_friction(&self, discard_friction: bool) {
2177 self.living_base().set_discard_friction(discard_friction);
2178 }
2179
2180 fn is_jumping(&self) -> bool {
2182 self.living_base().is_jumping()
2183 }
2184
2185 fn set_jumping(&self, jumping: bool) {
2187 self.living_base().set_jumping(jumping);
2188 }
2189
2190 fn travel_input(&self) -> LivingTravelInput {
2192 self.living_base().travel_input()
2193 }
2194
2195 fn set_travel_input(&self, input: LivingTravelInput) {
2197 self.living_base().set_travel_input(input);
2198 }
2199
2200 fn apply_input(&self) {
2202 self.living_base().dampen_travel_input();
2203 }
2204
2205 fn no_jump_delay(&self) -> i32 {
2207 self.living_base().no_jump_delay()
2208 }
2209
2210 fn set_no_jump_delay(&self, ticks: i32) {
2212 self.living_base().set_no_jump_delay(ticks);
2213 }
2214
2215 fn tick_no_jump_delay(&self) {
2217 self.living_base().tick_no_jump_delay();
2218 }
2219
2220 fn hurt_time(&self) -> i32 {
2222 self.living_base().hurt_time()
2223 }
2224
2225 fn default_is_immobile(&self) -> bool {
2228 self.is_dead_or_dying()
2229 }
2230
2231 fn is_immobile(&self) -> bool {
2233 self.default_is_immobile()
2234 }
2235
2236 fn apply_living_velocity_thresholds(&self) {
2239 let movement = self.velocity();
2240 let mut dx = movement.x;
2241 let mut dy = movement.y;
2242 let mut dz = movement.z;
2243
2244 if self.entity_type() == &vanilla_entities::PLAYER {
2245 if movement.x.mul_add(movement.x, movement.z * movement.z) < 9.0E-6 {
2246 dx = 0.0;
2247 dz = 0.0;
2248 }
2249 } else {
2250 if movement.x.abs() < 0.003 {
2251 dx = 0.0;
2252 }
2253 if movement.z.abs() < 0.003 {
2254 dz = 0.0;
2255 }
2256 }
2257
2258 if movement.y.abs() < 0.003 {
2259 dy = 0.0;
2260 }
2261
2262 self.set_velocity(DVec3::new(dx, dy, dz));
2263 }
2264
2265 fn server_ai_step(&self, _entity: &SharedEntity) {}
2267
2268 fn get_jump_boost_power(&self) -> f32 {
2270 self.mob_effect(vanilla_mob_effects::JUMP_BOOST)
2271 .map_or(0.0, |effect| 0.1 * (effect.amplifier() as f32 + 1.0))
2272 }
2273
2274 fn get_jump_power_with_multiplier(&self, multiplier: f32) -> f32 {
2277 let jump_strength =
2278 self.attributes()
2279 .lock()
2280 .get_value(vanilla_attributes::JUMP_STRENGTH)
2281 .unwrap_or(vanilla_attributes::JUMP_STRENGTH.default_value) as f32;
2282 jump_strength * multiplier * self.block_jump_factor() + self.get_jump_boost_power()
2283 }
2284
2285 fn get_jump_power(&self) -> f32 {
2287 self.get_jump_power_with_multiplier(1.0)
2288 }
2289
2290 fn default_jump_from_ground(&self) {
2292 let jump_power = self.get_jump_power();
2293 if jump_power <= 1.0E-5 {
2294 return;
2295 }
2296
2297 let movement = self.velocity();
2298 self.set_velocity(DVec3::new(
2299 movement.x,
2300 movement.y.max(f64::from(jump_power)),
2301 movement.z,
2302 ));
2303 if self.is_sprinting() {
2304 let angle = self.rotation().0.to_radians();
2305 self.set_velocity(
2306 self.velocity()
2307 + DVec3::new(
2308 -f64::from(trig::sin(angle)) * 0.2,
2309 0.0,
2310 f64::from(trig::cos(angle)) * 0.2,
2311 ),
2312 );
2313 }
2314
2315 self.mark_velocity_sync();
2316 }
2317
2318 fn jump_from_ground(&self) {
2320 self.default_jump_from_ground();
2321 }
2322
2323 fn go_down_in_water(&self) {
2325 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(-0.04_f32), 0.0));
2326 }
2327
2328 fn jump_in_liquid(&self, _fluid_tag: &Identifier) {
2330 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(0.04_f32), 0.0));
2331 }
2332
2333 fn handle_living_jump(&self) {
2336 if !self.is_jumping() || !self.is_affected_by_fluids() {
2337 self.set_no_jump_delay(0);
2338 return;
2339 }
2340
2341 let fluid_height = if self.is_in_lava() {
2342 self.fluid_contact().lava_height()
2343 } else {
2344 self.fluid_contact().water_height()
2345 };
2346 let in_water_and_has_fluid_height = self.is_in_water() && fluid_height > 0.0;
2347 let fluid_jump_threshold = self.get_fluid_jump_threshold();
2348 if !in_water_and_has_fluid_height
2349 || self.on_ground() && fluid_height <= fluid_jump_threshold
2350 {
2351 if !self.is_in_lava() || self.on_ground() && fluid_height <= fluid_jump_threshold {
2352 if (self.on_ground()
2353 || in_water_and_has_fluid_height && fluid_height <= fluid_jump_threshold)
2354 && self.no_jump_delay() == 0
2355 {
2356 self.jump_from_ground();
2357 self.set_no_jump_delay(10);
2358 }
2359 } else {
2360 self.jump_in_liquid(&vanilla_fluid_tags::FluidTag::LAVA);
2361 }
2362 } else {
2363 self.jump_in_liquid(&vanilla_fluid_tags::FluidTag::WATER);
2364 }
2365 }
2366
2367 fn tick_ridden(&self, _controller: &Player, _ridden_input: DVec3) {}
2369
2370 fn ridden_input(&self, _controller: &Player, self_input: DVec3) -> DVec3 {
2372 self_input
2373 }
2374
2375 fn ridden_speed(&self, _controller: &Player) -> f32 {
2377 self.get_speed()
2378 }
2379
2380 fn travel_ridden(
2382 &self,
2383 entity: &SharedEntity,
2384 controller: &Player,
2385 self_input: DVec3,
2386 ) -> Option<MoveResult> {
2387 let ridden_input = self.ridden_input(controller, self_input);
2388 self.tick_ridden(controller, ridden_input);
2389 if self.can_simulate_movement() {
2390 self.set_speed(self.ridden_speed(controller));
2391 return self.travel(entity, ridden_input);
2392 }
2393
2394 self.set_velocity(DVec3::ZERO);
2395 None
2396 }
2397
2398 fn default_ai_step(&self, entity: &SharedEntity) -> Option<MoveResult> {
2403 self.tick_no_jump_delay();
2404 if !self.can_simulate_movement() {
2405 self.set_velocity(self.velocity() * 0.98);
2406 }
2407
2408 self.tick_equipment();
2409 self.apply_living_velocity_thresholds();
2410 self.apply_input();
2411 if self.is_immobile() {
2412 self.set_jumping(false);
2413 let input = self.travel_input();
2414 self.set_travel_input(LivingTravelInput::new(0.0, input.vertical(), 0.0));
2415 } else if self.is_effective_ai() {
2416 self.server_ai_step(entity);
2417 }
2418
2419 self.handle_living_jump();
2420
2421 if self.is_fall_flying() {
2422 self.update_fall_flying();
2423 }
2424
2425 if self.has_mob_effect(vanilla_mob_effects::SLOW_FALLING)
2426 || self.has_mob_effect(vanilla_mob_effects::LEVITATION)
2427 {
2428 self.reset_fall_distance();
2429 }
2430
2431 let input = self.travel_input();
2432 let input = DVec3::new(
2433 f64::from(input.sideways()),
2434 f64::from(input.vertical()),
2435 f64::from(input.forward()),
2436 );
2437 let result = if Entity::is_alive(self)
2438 && let Some(controller_entity) = self.controlling_passenger()
2439 && let Some(controller) = controller_entity.as_player()
2440 {
2441 self.travel_ridden(entity, controller, input)
2442 } else if self.can_simulate_movement() && self.is_effective_ai() {
2443 self.travel(entity, input)
2444 } else {
2445 None
2446 };
2447
2448 self.apply_effects_from_blocks();
2449 self.tick_freezing();
2450 self.push_entities();
2451 result
2452 }
2453
2454 fn ai_step(&self, entity: &SharedEntity) -> Option<MoveResult> {
2456 self.default_ai_step(entity)
2457 }
2458
2459 fn push_entities(&self) {
2461 EntityPush::push_entities(self);
2462 }
2463
2464 fn push_entities_in_order(&self, entities: &[SharedEntity]) {
2466 EntityPush::push_entities_in_order(self.as_entity_event_source(), entities);
2467 }
2468
2469 fn apply_entity_cramming_damage(&self, world: &World, pushable_entities: &[SharedEntity]) {
2471 let max_cramming = world.get_game_rule(&MAX_ENTITY_CRAMMING);
2472
2473 if max_cramming <= 0 || pushable_entities.len() <= (max_cramming - 1) as usize {
2474 return;
2475 }
2476
2477 let random_roll = rand::random_range(0..4);
2478 let non_passenger_count = pushable_entities
2479 .iter()
2480 .filter(|entity| !entity.is_passenger())
2481 .count();
2482
2483 if should_apply_entity_cramming_damage(
2484 max_cramming,
2485 pushable_entities.len(),
2486 non_passenger_count,
2487 random_roll,
2488 ) {
2489 self.hurt(
2490 world,
2491 &DamageSource::environment(&vanilla_damage_types::CRAMMING),
2492 6.0,
2493 );
2494 }
2495 }
2496
2497 fn is_suppressing_sliding_down_ladder(&self) -> bool {
2499 self.is_suppressing_bounce()
2500 }
2501
2502 fn levitation_travel_y_delta(&self, movement_y: f64) -> Option<f64> {
2504 self.mob_effect(vanilla_mob_effects::LEVITATION)
2505 .map(|effect| (0.05 * f64::from(effect.amplifier() + 1) - movement_y) * 0.2)
2506 }
2507
2508 fn should_travel_in_fluid(&self, fluid_state: FluidState) -> bool {
2511 (self.is_in_water() || self.is_in_lava())
2512 && self.is_affected_by_fluids()
2513 && !self.can_stand_on_fluid(fluid_state)
2514 }
2515
2516 fn get_water_slow_down(&self) -> f32 {
2518 0.8
2519 }
2520
2521 fn water_movement_efficiency(&self) -> f32 {
2523 self.attributes()
2524 .lock()
2525 .get_value(vanilla_attributes::WATER_MOVEMENT_EFFICIENCY)
2526 .unwrap_or(0.0) as f32
2527 }
2528
2529 fn has_dolphins_grace(&self) -> bool {
2531 self.has_mob_effect(vanilla_mob_effects::DOLPHINS_GRACE)
2532 }
2533
2534 fn get_flying_speed(&self) -> f32 {
2537 if self
2538 .controlling_passenger()
2539 .is_some_and(|passenger| passenger.entity_type() == &vanilla_entities::PLAYER)
2540 {
2541 self.get_speed() * 0.1
2542 } else {
2543 0.02
2544 }
2545 }
2546
2547 fn get_friction_influenced_speed(&self, block_friction: f32) -> f32 {
2550 if self.on_ground() {
2551 self.get_speed() * (0.216_000_02 / (block_friction * block_friction * block_friction))
2552 } else {
2553 self.get_flying_speed()
2554 }
2555 }
2556
2557 fn air_travel_vertical_friction(&self, _horizontal_friction: f32) -> f32 {
2559 0.98
2561 }
2562
2563 fn handle_on_climbable(&self, movement: DVec3) -> DVec3 {
2567 if !self.on_climbable() {
2568 return movement;
2569 }
2570
2571 self.reset_fall_distance();
2572 let Some(world) = self.level() else {
2573 return movement;
2574 };
2575 let block_state = self.in_block_state(&world);
2576 let mut y = movement.y.max(-0.15);
2577 if y < 0.0
2578 && block_state.get_block() != &vanilla_blocks::SCAFFOLDING
2579 && self.is_suppressing_sliding_down_ladder()
2580 && self.entity_type() == &vanilla_entities::PLAYER
2581 {
2582 y = 0.0;
2583 }
2584
2585 DVec3::new(
2586 movement.x.clamp(-0.15, 0.15),
2587 y,
2588 movement.z.clamp(-0.15, 0.15),
2589 )
2590 }
2591
2592 fn apply_living_travel_gravity(&self) {
2594 let gravity = self.get_effective_gravity();
2595 if gravity != 0.0 {
2596 let mut velocity = self.velocity();
2597 velocity.y -= gravity;
2598 self.set_velocity(velocity);
2599 }
2600 }
2601
2602 fn handle_relative_friction_and_calculate_movement(
2604 &self,
2605 entity: &SharedEntity,
2606 input: DVec3,
2607 block_friction: f32,
2608 ) -> Option<(DVec3, MoveResult)> {
2609 self.move_relative(self.get_friction_influenced_speed(block_friction), input);
2610 self.set_velocity(self.handle_on_climbable(self.velocity()));
2611 let result = Arc::clone(entity).move_entity(MoverType::SelfMovement, self.velocity())?;
2612 let mut movement = self.velocity();
2613 if (result.horizontal_collision || self.is_jumping())
2614 && (self.on_climbable()
2615 || self.was_in_powder_snow()
2616 && PowderSnowBlock::can_entity_walk_on_powder_snow(self))
2617 {
2618 movement.y = 0.2;
2619 }
2620
2621 Some((movement, result))
2622 }
2623
2624 fn travel_in_air(&self, entity: &SharedEntity, input: DVec3) -> Option<MoveResult> {
2626 let world = self.level()?;
2627 let pos_below = self.block_pos_below_that_affects_movement()?;
2628 let block_friction = if self.on_ground() {
2629 world.get_block_state(pos_below).get_block().config.friction
2630 } else {
2631 1.0
2632 };
2633 let horizontal_friction = block_friction * 0.91;
2634 let (movement, result) =
2635 self.handle_relative_friction_and_calculate_movement(entity, input, block_friction)?;
2636 let movement_y = if let Some(levitation_y) = self.levitation_travel_y_delta(movement.y) {
2637 movement.y + levitation_y
2638 } else {
2639 movement.y - self.get_effective_gravity()
2640 };
2641
2642 if self.should_discard_friction() {
2643 self.set_velocity(DVec3::new(movement.x, movement_y, movement.z));
2644 } else {
2645 let vertical_friction = self.air_travel_vertical_friction(horizontal_friction);
2646 self.set_velocity(DVec3::new(
2647 movement.x * f64::from(horizontal_friction),
2648 movement_y * f64::from(vertical_friction),
2649 movement.z * f64::from(horizontal_friction),
2650 ));
2651 }
2652
2653 Some(result)
2654 }
2655
2656 fn get_fluid_falling_adjusted_movement(
2658 &self,
2659 base_gravity: f64,
2660 is_falling: bool,
2661 movement: DVec3,
2662 ) -> DVec3 {
2663 if base_gravity == 0.0 || self.is_sprinting() {
2664 return movement;
2665 }
2666
2667 let y = if is_falling
2668 && (movement.y - 0.005).abs() >= 0.003
2669 && (movement.y - base_gravity / 16.0).abs() < 0.003
2670 {
2671 -0.003
2672 } else {
2673 movement.y - base_gravity / 16.0
2674 };
2675
2676 DVec3::new(movement.x, y, movement.z)
2677 }
2678
2679 fn jump_out_of_fluid(&self, old_y: f64) {
2681 if !self.horizontal_collision() {
2682 return;
2683 }
2684
2685 let movement = self.velocity();
2686 let target_delta = DVec3::new(
2687 movement.x,
2688 movement.y + f64::from(0.6_f32) - self.position().y + old_y,
2689 movement.z,
2690 );
2691 if self.is_free(target_delta) {
2692 self.set_velocity(DVec3::new(movement.x, f64::from(0.3_f32), movement.z));
2693 }
2694 }
2695
2696 fn float_in_water_while_ridden(&self) {
2698 if !REGISTRY
2699 .entity_types
2700 .is_in_tag(self.entity_type(), &EntityTypeTag::CAN_FLOAT_WHILE_RIDDEN)
2701 {
2702 return;
2703 }
2704 if !self.is_vehicle()
2705 || self.fluid_contact().water_height() <= self.get_fluid_jump_threshold()
2706 {
2707 return;
2708 }
2709
2710 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(0.04_f32), 0.0));
2711 }
2712
2713 fn travel_in_water(
2715 &self,
2716 entity: &SharedEntity,
2717 input: DVec3,
2718 base_gravity: f64,
2719 is_falling: bool,
2720 old_y: f64,
2721 ) -> Option<MoveResult> {
2722 let mut slow_down = if self.is_sprinting() {
2723 0.9
2724 } else {
2725 self.get_water_slow_down()
2726 };
2727 let mut speed = 0.02;
2728 let mut water_movement_efficiency = self.water_movement_efficiency();
2729 if !self.on_ground() {
2730 water_movement_efficiency *= 0.5;
2731 }
2732
2733 if water_movement_efficiency > 0.0 {
2734 slow_down += (0.546_000_06 - slow_down) * water_movement_efficiency;
2735 speed += (self.get_speed() - speed) * water_movement_efficiency;
2736 }
2737
2738 if self.has_dolphins_grace() {
2739 slow_down = 0.96;
2740 }
2741
2742 self.move_relative(speed, input);
2743 let result = Arc::clone(entity).move_entity(MoverType::SelfMovement, self.velocity())?;
2744 let mut movement = self.velocity();
2745 if result.horizontal_collision && self.on_climbable() {
2746 movement.y = 0.2;
2747 }
2748
2749 movement = DVec3::new(
2750 movement.x * f64::from(slow_down),
2751 movement.y * f64::from(0.8_f32),
2752 movement.z * f64::from(slow_down),
2753 );
2754 self.set_velocity(self.get_fluid_falling_adjusted_movement(
2755 base_gravity,
2756 is_falling,
2757 movement,
2758 ));
2759 self.jump_out_of_fluid(old_y);
2760
2761 Some(result)
2762 }
2763
2764 fn travel_in_lava(
2766 &self,
2767 entity: &SharedEntity,
2768 input: DVec3,
2769 base_gravity: f64,
2770 is_falling: bool,
2771 old_y: f64,
2772 ) -> Option<MoveResult> {
2773 self.move_relative(0.02, input);
2774 let result = Arc::clone(entity).move_entity(MoverType::SelfMovement, self.velocity())?;
2775 if self.fluid_contact().lava_height() <= self.get_fluid_jump_threshold() {
2776 let movement = self.velocity();
2777 self.set_velocity(DVec3::new(
2778 movement.x * 0.5,
2779 movement.y * f64::from(0.8_f32),
2780 movement.z * 0.5,
2781 ));
2782 self.set_velocity(self.get_fluid_falling_adjusted_movement(
2783 base_gravity,
2784 is_falling,
2785 self.velocity(),
2786 ));
2787 } else {
2788 self.set_velocity(self.velocity() * 0.5);
2789 }
2790
2791 if base_gravity != 0.0 {
2792 self.set_velocity(self.velocity() + DVec3::new(0.0, -base_gravity / 4.0, 0.0));
2793 }
2794
2795 self.jump_out_of_fluid(old_y);
2796
2797 Some(result)
2798 }
2799
2800 fn travel_in_fluid(&self, entity: &SharedEntity, input: DVec3) -> Option<MoveResult> {
2802 let is_falling = self.velocity().y <= 0.0;
2803 let old_y = self.position().y;
2804 let base_gravity = self.get_effective_gravity();
2805 if self.is_in_water() {
2806 let result = self.travel_in_water(entity, input, base_gravity, is_falling, old_y);
2807 self.float_in_water_while_ridden();
2808 return result;
2809 }
2810
2811 self.travel_in_lava(entity, input, base_gravity, is_falling, old_y)
2812 }
2813
2814 fn update_fall_flying(&self) {
2816 self.check_fall_distance_accumulation();
2817 if self.can_glide() {
2818 if let Some(free_fall_interval) =
2819 fall_flying_free_fall_interval(self.fall_flying_ticks())
2820 {
2821 if free_fall_interval % 2 == 0 {
2822 self.damage_random_glider();
2823 }
2824 self.game_event(&vanilla_game_events::ELYTRA_GLIDE);
2825 }
2826 } else {
2827 self.set_fall_flying(false);
2828 }
2829 }
2830
2831 fn update_fall_flying_movement(&self, mut movement: DVec3) -> DVec3 {
2833 let look_angle = self.look_angle();
2834 let pitch_radians = self.rotation().1.to_radians();
2835 let look_horizontal_length = horizontal_distance(look_angle);
2836 let move_horizontal_length = horizontal_distance(movement);
2837 let gravity = self.get_effective_gravity();
2838 let lift_force = f64::from(pitch_radians).cos().powi(2);
2839 movement.y += gravity * (-1.0 + lift_force * 0.75);
2840
2841 if movement.y < 0.0 && look_horizontal_length > 0.0 {
2842 let convert = movement.y * -0.1 * lift_force;
2843 movement += DVec3::new(
2844 look_angle.x * convert / look_horizontal_length,
2845 convert,
2846 look_angle.z * convert / look_horizontal_length,
2847 );
2848 }
2849
2850 if pitch_radians < 0.0 && look_horizontal_length > 0.0 {
2851 let convert = move_horizontal_length * -f64::from(trig::sin(pitch_radians)) * 0.04;
2852 movement += DVec3::new(
2853 -look_angle.x * convert / look_horizontal_length,
2854 convert * 3.2,
2855 -look_angle.z * convert / look_horizontal_length,
2856 );
2857 }
2858
2859 if look_horizontal_length > 0.0 {
2860 movement += DVec3::new(
2861 (look_angle.x / look_horizontal_length * move_horizontal_length - movement.x) * 0.1,
2862 0.0,
2863 (look_angle.z / look_horizontal_length * move_horizontal_length - movement.z) * 0.1,
2864 );
2865 }
2866
2867 DVec3::new(
2868 movement.x * f64::from(0.99_f32),
2869 movement.y * f64::from(0.98_f32),
2870 movement.z * f64::from(0.99_f32),
2871 )
2872 }
2873
2874 fn stop_fall_flying(&self) {
2876 self.set_fall_flying(true);
2877 self.set_fall_flying(false);
2878 }
2879
2880 fn handle_fall_flying_collisions(
2882 &self,
2883 previous_horizontal_speed: f64,
2884 new_horizontal_speed: f64,
2885 ) {
2886 if !self.horizontal_collision() {
2887 return;
2888 }
2889
2890 let damage = fall_flying_collision_damage(previous_horizontal_speed, new_horizontal_speed);
2891 if damage <= 0.0 {
2892 return;
2893 }
2894
2895 self.play_sound(self.fall_damage_sound(damage as i32), 1.0, 1.0);
2896 if let Some(world) = self.level() {
2897 self.hurt(
2898 &world,
2899 &DamageSource::environment(&vanilla_damage_types::FLY_INTO_WALL),
2900 damage,
2901 );
2902 }
2903 }
2904
2905 fn travel_fall_flying(&self, entity: &SharedEntity, input: DVec3) -> Option<MoveResult> {
2907 if self.on_climbable() {
2908 let result = self.travel_in_air(entity, input);
2909 self.stop_fall_flying();
2910 return result;
2911 }
2912
2913 let previous_movement = self.velocity();
2914 let previous_horizontal_speed = horizontal_distance(previous_movement);
2915 self.set_velocity(self.update_fall_flying_movement(previous_movement));
2916 let result = Arc::clone(entity).move_entity(MoverType::SelfMovement, self.velocity());
2917 let new_horizontal_speed = horizontal_distance(self.velocity());
2918 self.handle_fall_flying_collisions(previous_horizontal_speed, new_horizontal_speed);
2919 result
2920 }
2921
2922 fn default_travel(&self, entity: &SharedEntity, input: DVec3) -> Option<MoveResult> {
2925 let world = self.level()?;
2926 let fluid_state = get_fluid_state(&world, self.block_position());
2927 if self.should_travel_in_fluid(fluid_state) {
2928 return self.travel_in_fluid(entity, input);
2929 }
2930 if self.is_fall_flying() {
2931 return self.travel_fall_flying(entity, input);
2932 }
2933
2934 self.travel_in_air(entity, input)
2935 }
2936
2937 fn travel(&self, entity: &SharedEntity, input: DVec3) -> Option<MoveResult> {
2939 self.default_travel(entity, input)
2940 }
2941
2942 fn sleeping_pos(&self) -> Option<BlockPos> {
2944 self.living_base().sleeping_pos()
2945 }
2946
2947 fn set_sleeping_pos(&self, bed_position: BlockPos) {
2949 self.living_base().set_sleeping_pos(bed_position);
2950 if let Some(entity_data) = self.living_synced_data() {
2951 entity_data.set_sleeping_pos(bed_position);
2952 }
2953 }
2954
2955 fn clear_sleeping_pos(&self) {
2957 self.living_base().clear_sleeping_pos();
2958 if let Some(entity_data) = self.living_synced_data() {
2959 entity_data.clear_sleeping_pos();
2960 }
2961 }
2962
2963 fn is_sleeping(&self) -> bool {
2965 self.sleeping_pos().is_some()
2966 }
2967
2968 fn living_synced_data(&self) -> Option<&dyn LivingEntitySyncedData> {
2971 None
2972 }
2973
2974 fn start_sleeping(&self, bed_position: BlockPos) -> Result<(), EntityMoveError> {
2976 let Some(world) = self.level() else {
2977 return Err(EntityMoveError::NotLive {
2978 entity_id: self.id(),
2979 });
2980 };
2981 let block_state = world.get_block_state(bed_position);
2982
2983 let Some(sleep_height) = BLOCK_BEHAVIORS
2984 .get_behavior(block_state.get_block())
2985 .get_sleep_height(block_state, world.as_ref(), bed_position)
2986 else {
2987 return Err(EntityMoveError::InvalidSleepPosition {
2988 entity_id: self.id(),
2989 bed_position,
2990 });
2991 };
2992
2993 if self.is_passenger() {
2994 self.stop_riding();
2995 }
2996
2997 self.try_set_position(DVec3::new(
2998 f64::from(bed_position.x()) + 0.5,
2999 f64::from(bed_position.y()) + sleep_height + BED_SLEEP_POSITION_OFFSET,
3000 f64::from(bed_position.z()) + 0.5,
3001 ))?;
3002
3003 if block_state.is_bed() {
3004 world.set_block(
3005 bed_position,
3006 block_state.set_value(&BlockStateProperties::OCCUPIED, true),
3007 UpdateFlags::UPDATE_ALL,
3008 );
3009 }
3010
3011 self.set_pose(EntityPose::Sleeping);
3012 self.set_sleeping_pos(bed_position);
3013 self.set_velocity(DVec3::ZERO);
3014 Ok(())
3015 }
3016
3017 fn default_stop_sleeping(&self) {
3019 if let Some(bed_position) = self.sleeping_pos()
3020 && let Some(world) = self.level()
3021 {
3022 let state = world.get_block_state(bed_position);
3023 if state.is_bed() {
3024 let facing = state.get_value(&BlockStateProperties::HORIZONTAL_FACING);
3025 world.set_block(
3026 bed_position,
3027 state.set_value(&BlockStateProperties::OCCUPIED, false),
3028 UpdateFlags::UPDATE_ALL,
3029 );
3030 BLOCK_BEHAVIORS
3031 .get_behavior(state.get_block())
3032 .on_stop_sleeping(state, &world, bed_position);
3033 let stand_up = BedBlock::find_standup_position(
3034 &world,
3035 self.as_entity_event_source(),
3036 facing,
3037 bed_position,
3038 )
3039 .unwrap_or_else(|| {
3040 let above = bed_position.above();
3041 DVec3::new(
3042 f64::from(above.x()) + 0.5,
3043 f64::from(above.y()) + 0.1,
3044 f64::from(above.z()) + 0.5,
3045 )
3046 });
3047 let bed_center = DVec3::new(
3048 f64::from(bed_position.x()) + 0.5,
3049 f64::from(bed_position.y()),
3050 f64::from(bed_position.z()) + 0.5,
3051 );
3052 let look_direction = (bed_center - stand_up).normalize_or_zero();
3053 let yaw = wrap_degrees(
3054 (look_direction.z.atan2(look_direction.x).to_degrees() - f64::from(DEGREE_90))
3055 as f32,
3056 );
3057 if let Err(error) = self.try_set_position(stand_up) {
3058 log::warn!(
3059 "failed to move entity {} to bed stand-up position: {error}",
3060 self.id()
3061 );
3062 }
3063 self.set_rotation((yaw, 0.0));
3064 }
3065 }
3066
3067 self.set_pose(EntityPose::Standing);
3068 self.clear_sleeping_pos();
3069 }
3070
3071 fn stop_sleeping(&self) {
3073 self.default_stop_sleeping();
3074 }
3075
3076 fn is_sprinting(&self) -> bool {
3078 self.living_base().is_sprinting()
3079 }
3080
3081 fn set_sprinting(&self, sprinting: bool) {
3083 self.set_shared_sprinting(sprinting);
3084 self.living_base().set_sprinting(sprinting);
3085 }
3086
3087 fn get_speed(&self) -> f32 {
3089 self.living_base().speed()
3090 }
3091
3092 fn set_speed(&self, speed: f32) {
3094 self.living_base().set_speed(speed);
3095 }
3096
3097 fn apply_post_impulse_grace_time(&self, ticks: i32) {
3099 self.living_base().apply_post_impulse_grace_time(ticks);
3100 }
3101
3102 fn set_ignore_fall_damage_from_current_impulse(
3104 &self,
3105 ignore_fall_damage: bool,
3106 new_impulse_impact_pos: DVec3,
3107 ) {
3108 self.living_base()
3109 .set_ignore_fall_damage_from_current_impulse(
3110 ignore_fall_damage,
3111 new_impulse_impact_pos,
3112 );
3113 }
3114
3115 fn is_ignoring_fall_damage_from_current_impulse(&self) -> bool {
3118 self.living_base()
3119 .is_ignoring_fall_damage_from_current_impulse()
3120 }
3121
3122 fn current_impulse_impact_pos(&self) -> Option<DVec3> {
3124 self.living_base().current_impulse_impact_pos()
3125 }
3126
3127 fn try_reset_current_impulse_context(&self) {
3129 self.living_base().try_reset_current_impulse_context();
3130 }
3131
3132 fn reset_current_impulse_context(&self) {
3134 self.living_base().reset_current_impulse_context();
3135 }
3136
3137 fn is_in_post_impulse_grace_time(&self) -> bool {
3139 self.living_base().is_in_post_impulse_grace_time()
3140 }
3141
3142 fn tick_post_impulse_grace_time(&self) {
3144 self.living_base().tick_post_impulse_grace_time();
3145 }
3146
3147 fn refresh_dirty_attributes(&self) {
3149 let dirty = self.attributes().lock().drain_dirty_updates();
3150 for attr in dirty {
3151 if attr.key == vanilla_attributes::MAX_HEALTH.key {
3152 let max = self.get_max_health();
3153 if self.get_health() > max {
3154 self.set_health(max);
3155 }
3156 } else if attr.key == vanilla_attributes::MAX_ABSORPTION.key {
3157 let max = self
3158 .attributes()
3159 .lock()
3160 .get_value(vanilla_attributes::MAX_ABSORPTION)
3161 .unwrap_or(0.0) as f32;
3162 if self.get_absorption_amount() > max {
3163 self.set_absorption_amount(max);
3164 }
3165 } else if attr.key == vanilla_attributes::SCALE.key {
3166 self.refresh_dimensions();
3167 }
3168 }
3170 }
3171
3172 fn random_teleport(&self, world: &Arc<World>, x: f64, y: f64, z: f64, broadcast: bool) -> bool {
3175 let Some(landing_y) = random_teleport_ground_y(world, x, y, z) else {
3176 return false;
3177 };
3178
3179 let dimensions = self.base().dimensions();
3180 let aabb = WorldAabb::entity_box(
3181 x,
3182 landing_y,
3183 z,
3184 f64::from(dimensions.half_width()),
3185 f64::from(dimensions.height),
3186 );
3187 let collision = WorldCollisionProvider::for_entity(world, self.as_entity_event_source());
3188 if collision.has_entity_collision(&aabb)
3189 || collision.has_block_collision_with_context(&aabb, BlockCollisionContext::empty())
3190 || aabb_contains_any_liquid(world, aabb)
3191 {
3192 return false;
3193 }
3194
3195 if self.teleport_to(DVec3::new(x, landing_y, z)).is_err() {
3196 return false;
3197 }
3198
3199 if let Some(pathfinder) = self.as_pathfinder_mob() {
3200 pathfinder.mob_base().navigation().lock().stop();
3201 }
3202
3203 if broadcast {
3204 self.broadcast_entity_event(EntityStatus::Teleport);
3205 }
3206 true
3207 }
3208}
3209
3210fn random_teleport_ground_y(world: &Arc<World>, x: f64, y: f64, z: f64) -> Option<f64> {
3214 let mut pos = BlockPos::containing(x, y, z);
3215 let mut current_y = y;
3216 while pos.y() > world.get_min_y() {
3217 let below = pos.below();
3218 if world.get_block_state(below).blocks_motion() {
3219 return Some(current_y);
3220 }
3221 current_y -= 1.0;
3222 pos = below;
3223 }
3224 None
3225}
3226
3227fn death_loot_items_with_rng<R: rand::Rng, E: LivingEntity + ?Sized>(
3228 entity: &E,
3229 loot_table: LootTableRef,
3230 world: &World,
3231 source: &DamageSource,
3232 killed_by_player: bool,
3233 rng: &mut R,
3234) -> Vec<ItemStack> {
3235 let causing_entity = source.causing_entity().map(AsRef::as_ref);
3236 let direct_entity = source.direct_entity().map(AsRef::as_ref);
3237 let last_damage_player = if killed_by_player {
3238 entity.living_base().last_hurt_by_player(world)
3239 } else {
3240 None
3241 };
3242
3243 let position = entity.position();
3244 let this_equipment = living_loot_equipment(entity);
3245 let causing_equipment = causing_entity.and_then(loot_equipment);
3246 let direct_equipment = direct_entity.and_then(loot_equipment);
3247 let last_damage_player_equipment = last_damage_player.as_deref().and_then(loot_equipment);
3248 let this_entity = entity_loot_ref(entity.as_entity_event_source(), Some(&this_equipment));
3249 let causing_entity =
3250 causing_entity.map(|causing| entity_loot_ref(causing, causing_equipment.as_ref()));
3251 let direct_entity =
3252 direct_entity.map(|direct| entity_loot_ref(direct, direct_equipment.as_ref()));
3253 let last_damage_player = last_damage_player
3254 .as_deref()
3255 .map(|player| entity_loot_ref(player, last_damage_player_equipment.as_ref()));
3256 let damage_source = DamageSourceInfo {
3257 damage_type: Some(&source.damage_type.key),
3258 tags: &[],
3259 is_direct: source.is_direct(),
3260 };
3261
3262 let mut context = LootContext::new(rng)
3263 .with_origin(position.x, position.y, position.z)
3264 .with_game_time(world.game_time())
3265 .with_killed_by_player(killed_by_player)
3266 .with_this_entity(this_entity)
3267 .with_damage_source(damage_source);
3268 if let Some(entity) = causing_entity {
3269 context = context.with_killer_entity(entity);
3270 }
3271 if let Some(entity) = direct_entity {
3272 context = context.with_direct_killer_entity(entity);
3273 }
3274 if let Some(entity) = last_damage_player {
3275 context = context.with_last_damage_player(entity);
3276 }
3277
3278 loot_table.get_random_items(&mut context)
3279}
3280
3281pub(crate) fn shearing_loot_items_with_rng<R: rand::Rng, E: LivingEntity + ?Sized>(
3284 entity: &E,
3285 loot_table: LootTableRef,
3286 tool: &ItemStack,
3287 rng: &mut R,
3288) -> Vec<ItemStack> {
3289 let position = entity.position();
3290 let equipment = living_loot_equipment(entity);
3291 let mut context = LootContext::new(rng)
3292 .with_origin(position.x, position.y, position.z)
3293 .with_this_entity(entity_loot_ref(
3294 entity.as_entity_event_source(),
3295 Some(&equipment),
3296 ))
3297 .with_tool(tool);
3298 if let Some(level) = entity.level() {
3299 context = context.with_game_time(level.game_time());
3300 }
3301 loot_table.get_random_items(&mut context)
3302}