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