1use super::*;
2
3pub trait LivingEntity: Entity {
11 fn living_base(&self) -> &LivingEntityBase;
15
16 fn living_rotation_state(&self) -> LivingRotationState {
18 self.living_base().rotation_state()
19 }
20
21 fn y_body_rot(&self) -> f32 {
23 self.living_base().y_body_rot()
24 }
25
26 fn set_y_body_rot(&self, y_body_rot: f32) {
28 self.living_base().set_y_body_rot(y_body_rot);
29 }
30
31 fn y_head_rot(&self) -> f32 {
33 self.living_base().y_head_rot()
34 }
35
36 fn set_y_head_rot(&self, y_head_rot: f32) {
38 self.living_base().set_y_head_rot(y_head_rot);
39 }
40
41 fn advance_living_rotation_for_base_tick(&self) {
43 self.living_base().advance_rotation_for_base_tick();
44 }
45
46 fn advance_attack_animation_for_base_tick(&self) {
48 self.living_base().advance_attack_animation_for_base_tick();
49 }
50
51 fn base_tick_living_entity(&self) {
53 self.advance_living_rotation_for_base_tick();
54 self.advance_attack_animation_for_base_tick();
55 self.entity_base_tick();
56 self.tick_living_environmental_damage();
57 }
58
59 fn living_swing_state(&self) -> LivingSwingState {
61 self.living_base().swing_state()
62 }
63
64 fn current_swing_duration(&self) -> i32 {
66 let hand = self
67 .living_swing_state()
68 .swinging_arm()
69 .unwrap_or(InteractionHand::MainHand);
70 let slot = match hand {
71 InteractionHand::MainHand => EquipmentSlot::MainHand,
72 InteractionHand::OffHand => EquipmentSlot::OffHand,
73 };
74 let mut swing_duration = SwingAnimation::DEFAULT.duration;
75 self.with_equipment_slot(slot, &mut |item_stack| {
76 swing_duration = item_stack
77 .get(SWING_ANIMATION)
78 .copied()
79 .unwrap_or(SwingAnimation::DEFAULT)
80 .duration;
81 });
82 if let Some(haste) = self.mob_effect(vanilla_mob_effects::HASTE) {
83 swing_duration - (1 + haste.amplifier())
84 } else if let Some(mining_fatigue) = self.mob_effect(vanilla_mob_effects::MINING_FATIGUE) {
85 swing_duration + (1 + mining_fatigue.amplifier()) * 2
86 } else {
87 swing_duration
88 }
89 }
90
91 fn swing(&self, hand: InteractionHand, update_self: bool) {
93 if !self
94 .living_base()
95 .start_swing(hand, self.current_swing_duration())
96 {
97 return;
98 }
99
100 let Some(world) = self.level() else {
101 return;
102 };
103 let action = match hand {
104 InteractionHand::MainHand => AnimateAction::SwingMainHand,
105 InteractionHand::OffHand => AnimateAction::SwingOffHand,
106 };
107 let packet = CAnimate::new(self.id(), action);
108 let exclude = if update_self { None } else { Some(self.id()) };
109 world.broadcast_to_entity_trackers(self.id(), packet.clone(), exclude);
110 if update_self && let Some(player) = self.as_player() {
111 player.send_packet(packet);
112 }
113 }
114
115 fn update_swing_time(&self) {
117 self.living_base()
118 .update_swing_time(self.current_swing_duration());
119 }
120
121 fn attributes(&self) -> &SyncMutex<AttributeMap> {
123 self.living_base().attributes()
124 }
125
126 fn pack_syncable_attributes(&self) -> Vec<AttributeSnapshot> {
131 self.attributes().lock().syncable_snapshots()
132 }
133
134 fn drain_dirty_syncable_attributes(&self) -> Vec<AttributeSnapshot> {
139 self.attributes().lock().drain_dirty_sync()
140 }
141
142 fn drain_dirty_mob_effects(&self) -> Vec<MobEffectSyncChange> {
144 self.living_base().drain_dirty_mob_effects()
145 }
146
147 fn pack_all_equipment(&self) -> Vec<EquipmentSlotItem> {
149 self.pack_living_equipment()
150 }
151
152 fn drain_dirty_equipment(&self) -> Vec<EquipmentSlotItem> {
154 self.drain_dirty_living_equipment()
155 }
156
157 fn save_command_nbt(&self, nbt: &mut NbtCompound) {
159 nbt.insert("Health", self.get_health());
160 nbt.insert(
161 "DeathTime",
162 NbtTag::Short(self.living_base().death_time() as i16),
163 );
164 nbt.insert("AbsorptionAmount", self.get_absorption_amount());
165 nbt.insert(
166 "current_impulse_context_reset_grace_time",
167 self.living_base()
168 .current_impulse_context_reset_grace_time(),
169 );
170 if let Some(impact) = self.living_base().current_impulse_impact_pos() {
171 nbt.insert(
172 "current_explosion_impact_pos",
173 NbtList::Double(vec![impact.x, impact.y, impact.z]),
174 );
175 }
176 nbt.insert("attributes", self.attributes().lock().to_vanilla_nbt());
177
178 let mut effects = self.living_base().active_mob_effects();
179 effects.sort_by_key(|effect| effect.effect().try_id().unwrap_or(usize::MAX));
180 if !effects.is_empty() {
181 nbt.insert(
182 "active_effects",
183 NbtList::Compound(
184 effects
185 .iter()
186 .map(ActiveMobEffect::to_vanilla_nbt)
187 .collect(),
188 ),
189 );
190 }
191
192 nbt.insert("FallFlying", nbt_bool(self.is_fall_flying()));
193 if let Some(pos) = self.sleeping_pos() {
194 nbt.insert(
195 "sleeping_pos",
196 NbtTag::IntArray(vec![pos.x(), pos.y(), pos.z()]),
197 );
198 }
199 if let Some(uuid) = self.last_hurt_by_player_uuid() {
200 nbt.insert(
201 "last_hurt_by_player",
202 NbtTag::IntArray(uuid.to_int_array().to_vec()),
203 );
204 nbt.insert(
205 "last_hurt_by_player_memory_time",
206 self.last_hurt_by_player_memory_time(),
207 );
208 }
209 if let Some(entity) = self.last_hurt_by_mob() {
210 nbt.insert(
211 "last_hurt_by_mob",
212 NbtTag::IntArray(entity.uuid().to_int_array().to_vec()),
213 );
214 nbt.insert(
215 "ticks_since_last_hurt_by_mob",
216 self.tick_count()
217 .wrapping_sub(self.last_hurt_by_mob_timestamp()),
218 );
219 }
220
221 let mut equipment = NbtCompound::new();
222 for slot in EquipmentSlot::ALL {
223 self.with_equipment_slot(slot, &mut |item| {
224 if !item.is_empty() {
225 equipment.insert(slot.name(), item.to_nbt_tag_ref());
226 }
227 });
228 }
229 if !equipment.is_empty() {
230 nbt.insert("equipment", NbtTag::Compound(equipment));
231 }
232 }
233
234 fn get_health(&self) -> f32;
236
237 fn set_health(&self, health: f32);
239
240 fn get_max_health(&self) -> f32 {
242 self.attributes()
243 .lock()
244 .required_value(vanilla_attributes::MAX_HEALTH) as f32
245 }
246
247 fn no_action_time(&self) -> i32 {
249 self.living_base().no_action_time()
250 }
251
252 fn set_no_action_time(&self, no_action_time: i32) {
254 self.living_base().set_no_action_time(no_action_time);
255 }
256
257 fn increment_no_action_time(&self) {
259 self.living_base().increment_no_action_time();
260 }
261
262 fn heal(&self, amount: f32) {
264 let current_health = self.get_health();
265 if current_health > 0.0 {
266 self.set_health(current_health + amount);
267 }
268 }
269
270 fn is_dead_or_dying(&self) -> bool {
272 self.get_health() <= 0.0
273 }
274
275 fn is_baby(&self) -> bool {
277 self.as_ageable_mob().is_some_and(AgeableMob::is_baby)
278 }
279
280 fn sound_volume(&self) -> f32 {
282 1.0
283 }
284
285 fn voice_pitch(&self) -> f32 {
287 if self.is_baby() {
288 (rand::random::<f32>() - rand::random::<f32>()) * 0.2 + 1.5
289 } else {
290 (rand::random::<f32>() - rand::random::<f32>()) * 0.2 + 1.0
291 }
292 }
293
294 fn hurt_sound(&self, _source: &DamageSource) -> Option<SoundEventRef> {
296 Some(&sound_events::ENTITY_GENERIC_HURT)
297 }
298
299 fn death_sound(&self) -> Option<SoundEventRef> {
301 Some(&sound_events::ENTITY_GENERIC_DEATH)
302 }
303
304 fn make_sound(&self, sound: Option<SoundEventRef>) {
306 if let Some(sound) = sound {
307 self.play_sound(sound, self.sound_volume(), self.voice_pitch());
308 }
309 }
310
311 fn play_hurt_sound(&self, source: &DamageSource) {
313 if let Some(mob) = self.as_mob() {
314 mob.reset_ambient_sound_time();
315 }
316 self.make_sound(self.hurt_sound(source));
317 }
318
319 fn play_death_sound(&self) {
321 self.make_sound(self.death_sound());
322 }
323
324 fn get_age_scale(&self) -> f32 {
326 if self.is_baby() { 0.5 } else { 1.0 }
327 }
328
329 fn get_scale(&self) -> f32 {
331 self.attributes()
332 .lock()
333 .get_value(vanilla_attributes::SCALE)
334 .unwrap_or(1.0) as f32
335 }
336
337 fn is_alive(&self) -> bool {
339 !self.is_dead_or_dying()
340 }
341
342 fn get_armor_cover_percentage(&self) -> f32 {
344 let mut covered_slots = 0;
345 for slot in EquipmentSlot::ARMOR_SLOTS {
346 self.with_equipment_slot(slot, &mut |item_stack| {
347 if !item_stack.is_empty() {
348 covered_slots += 1;
349 }
350 });
351 }
352
353 covered_slots as f32 / EquipmentSlot::ARMOR_SLOTS.len() as f32
354 }
355
356 fn get_visibility_percent(&self, targeting_entity: Option<&dyn Entity>) -> f64 {
358 let mut visibility_percent = 1.0;
359 if self.is_discrete() {
360 visibility_percent *= 0.8;
361 }
362
363 if self.is_invisible() {
364 visibility_percent *= 0.7 * f64::from(self.get_armor_cover_percentage().max(0.1));
365 }
366
367 if self.disguise_head_matches_targeting_entity(targeting_entity) {
368 visibility_percent *= 0.5;
369 }
370
371 visibility_percent
372 }
373
374 fn disguise_head_matches_targeting_entity(
376 &self,
377 targeting_entity: Option<&dyn Entity>,
378 ) -> bool {
379 let Some(targeting_entity) = targeting_entity else {
380 return false;
381 };
382
383 let mut matches_target = false;
384 self.with_equipment_slot(EquipmentSlot::Head, &mut |item_stack| {
385 let target_type = targeting_entity.entity_type();
386 matches_target = target_type == &vanilla_entities::SKELETON
387 && item_stack.is(&vanilla_items::SKELETON_SKULL)
388 || target_type == &vanilla_entities::ZOMBIE
389 && item_stack.is(&vanilla_items::ZOMBIE_HEAD)
390 || target_type == &vanilla_entities::PIGLIN
391 && item_stack.is(&vanilla_items::PIGLIN_HEAD)
392 || target_type == &vanilla_entities::PIGLIN_BRUTE
393 && item_stack.is(&vanilla_items::PIGLIN_HEAD)
394 || target_type == &vanilla_entities::CREEPER
395 && item_stack.is(&vanilla_items::CREEPER_HEAD);
396 });
397 matches_target
398 }
399
400 fn can_be_seen_by_anyone(&self) -> bool {
402 !self.is_spectator() && Entity::is_alive(self)
403 }
404
405 fn can_be_seen_as_enemy(&self) -> bool {
407 !self.is_invulnerable() && self.can_be_seen_by_anyone()
408 }
409
410 fn can_attack(&self, target: &dyn LivingEntity) -> bool {
412 if target.entity_type() == &vanilla_entities::PLAYER
413 && self
414 .level()
415 .is_some_and(|world| world.difficulty() == Difficulty::Peaceful)
416 {
417 return false;
418 }
419
420 target.can_be_seen_as_enemy()
421 }
422
423 fn last_damage_source(&self) -> Option<DamageSource> {
425 let game_time = self.level().map_or(0, |world| world.game_time());
426 self.living_base().last_damage_source(game_time)
427 }
428
429 fn set_last_hurt_by_player(&self, player_uuid: Uuid, time_to_remember: i32) {
431 self.living_base()
432 .set_last_hurt_by_player(player_uuid, time_to_remember);
433 }
434
435 fn last_hurt_by_player_memory_time(&self) -> i32 {
437 self.living_base().last_hurt_by_player_memory_time()
438 }
439
440 fn last_hurt_by_player_uuid(&self) -> Option<Uuid> {
442 self.living_base().last_hurt_by_player_uuid()
443 }
444
445 fn last_hurt_by_mob(&self) -> Option<SharedEntity> {
447 self.living_base().last_hurt_by_mob()
448 }
449
450 fn last_hurt_by_mob_timestamp(&self) -> i32 {
452 self.living_base().last_hurt_by_mob_timestamp()
453 }
454
455 fn set_last_hurt_by_mob(&self, target: Option<&SharedEntity>) {
457 self.living_base()
458 .set_last_hurt_by_mob(target, self.tick_count());
459 }
460
461 fn last_hurt_mob(&self) -> Option<SharedEntity> {
463 self.living_base().last_hurt_mob()
464 }
465
466 fn last_hurt_mob_timestamp(&self) -> i32 {
468 self.living_base().last_hurt_mob_timestamp()
469 }
470
471 fn set_last_hurt_mob(&self, target: Option<&SharedEntity>) {
473 self.living_base()
474 .set_last_hurt_mob(target, self.tick_count());
475 }
476
477 fn resolve_mob_responsible_for_damage(&self, world: &World, source: &DamageSource) {
479 if source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_ANGER) {
480 return;
481 }
482 if source.damage_type == &vanilla_damage_types::WIND_CHARGE
483 && REGISTRY.entity_types.is_in_tag(
484 self.entity_type(),
485 &EntityTypeTag::NO_ANGER_FROM_WIND_CHARGE,
486 )
487 {
488 return;
489 }
490
491 let Some(entity_id) = source.causing_entity_id else {
492 return;
493 };
494 let Some(entity) = world.get_entity_by_id(entity_id) else {
495 return;
496 };
497 if entity.is_living_entity() {
498 self.set_last_hurt_by_mob(Some(&entity));
499 }
500 }
501
502 fn resolve_player_responsible_for_damage(&self, world: &World, source: &DamageSource) {
504 let Some(entity_id) = source.causing_entity_id else {
505 return;
506 };
507 let Some(entity) = world.get_entity_by_id(entity_id) else {
508 return;
509 };
510 if entity.entity_type() == &vanilla_entities::PLAYER {
511 self.set_last_hurt_by_player(entity.uuid(), 100);
512 }
513 }
514
515 fn has_line_of_sight(&self, target: &dyn Entity) -> bool {
517 self.has_line_of_sight_with(
518 target,
519 ClipBlockShape::Collider,
520 ClipFluid::None,
521 target.get_eye_y(),
522 )
523 }
524
525 fn has_line_of_sight_with(
527 &self,
528 target: &dyn Entity,
529 block_shape: ClipBlockShape,
530 fluid: ClipFluid,
531 target_eye_y: f64,
532 ) -> bool {
533 let Some(world) = self.level() else {
534 return false;
535 };
536 let Some(target_world) = target.level() else {
537 return false;
538 };
539 if !Arc::ptr_eq(&world, &target_world) {
540 return false;
541 }
542
543 let position = self.position();
544 let target_position = target.position();
545 let start = DVec3::new(position.x, self.get_eye_y(), position.z);
546 let end = DVec3::new(target_position.x, target_eye_y, target_position.z);
547 if start.distance_squared(end) > 128.0 * 128.0 {
548 return false;
549 }
550
551 world.clip(start, end, block_shape, fluid).is_miss()
552 }
553
554 fn default_is_invulnerable_to(&self, source: &DamageSource) -> bool {
556 self.is_removed()
557 || self.is_invulnerable() && !source.bypasses_invulnerability()
558 || source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FIRE) && self.fire_immune()
559 || source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FALL)
560 && self.is_fall_damage_immune()
561 }
562
563 fn is_invulnerable_to(&self, world: &World, source: &DamageSource) -> bool {
565 self.default_is_invulnerable_to(source)
566 || enchantment_helper::is_immune_to_damage(world, self, source)
567 }
568
569 fn hurt_server(&self, world: &World, source: &DamageSource, amount: f32) -> bool {
574 if self.is_invulnerable_to(world, source) {
575 return false;
576 }
577 if self.is_dead_or_dying() {
578 return false;
579 }
580 if source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FIRE)
581 && self.has_mob_effect(vanilla_mob_effects::FIRE_RESISTANCE)
582 {
583 return false;
584 }
585 if self.is_sleeping() {
586 self.stop_sleeping();
587 }
588
589 self.set_no_action_time(0);
590
591 let mut damage = amount;
592 if damage < 0.0 {
593 damage = 0.0;
594 }
595
596 if source.is(&vanilla_damage_type_tags::DamageTypeTag::IS_FREEZING)
598 && REGISTRY
599 .entity_types
600 .is_in_tag(self.entity_type(), &EntityTypeTag::FREEZE_HURTS_EXTRA_TYPES)
601 {
602 damage *= 5.0;
603 }
604 if !damage.is_finite() {
606 damage = f32::MAX;
607 }
608
609 let Some((took_full_damage, effective_amount)) = self
610 .living_base()
611 .apply_damage_cooldown(damage, source.bypasses_cooldown())
612 else {
613 return false;
614 };
615
616 self.before_actually_hurt(source, effective_amount);
617 self.actually_hurt(world, source, effective_amount);
618 self.resolve_mob_responsible_for_damage(world, source);
619 self.resolve_player_responsible_for_damage(world, source);
620
621 if took_full_damage {
622 self.broadcast_damage_event(world, source);
623 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_IMPACT) {
624 self.mark_hurt();
625 self.broadcast_hurt_animation(world);
626 }
627 self.apply_damage_knockback(source);
628 }
629
630 if self.is_dead_or_dying() {
631 if took_full_damage {
632 self.play_death_sound();
633 }
634 self.die(source);
635 } else if took_full_damage {
636 self.play_hurt_sound(source);
637 }
638 let game_time = self.level().map_or(0, |world| world.game_time());
641 self.living_base()
642 .record_last_damage_source(source, game_time);
643
644 true
645 }
646
647 fn before_actually_hurt(&self, _source: &DamageSource, _amount: f32) {
649 if let Some(animal) = self.as_animal() {
650 animal.reset_love();
651 }
652 }
653
654 fn hurt_armor(&self, _source: &DamageSource, _damage: f32) {}
656
657 fn do_hurt_equipment(&self, source: &DamageSource, damage: f32, slots: &[EquipmentSlot]) {
659 if damage <= 0.0 {
660 return;
661 }
662
663 let durability_damage = (damage / 4.0).max(1.0) as i32;
664 for &slot in slots {
665 let mut item_broke = false;
666 self.with_equipment_slot_mut(slot, &mut |item| {
667 let damage_on_hurt = item
668 .get_equippable()
669 .is_some_and(|equippable| equippable.damage_on_hurt);
670 if damage_on_hurt
671 && item.is_damageable_item()
672 && item.can_be_hurt_by(source.damage_type)
673 {
674 item_broke =
675 item.hurt_and_break(durability_damage, self.has_infinite_materials());
676 }
677 });
678 if item_broke {
679 self.on_equipped_item_broken(slot);
680 }
681 }
682 }
683
684 fn get_damage_after_armor_absorb(&self, source: &DamageSource, mut damage: f32) -> f32 {
686 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_ARMOR) {
687 self.hurt_armor(source, damage);
688 let armor_toughness =
689 self.attributes()
690 .lock()
691 .required_value(vanilla_attributes::ARMOR_TOUGHNESS) as f32;
692 damage = combat_rules::get_damage_after_absorb(
693 self,
694 damage,
695 source,
696 self.get_armor_value() as f32,
697 armor_toughness,
698 );
699 }
700 damage
701 }
702
703 fn get_damage_after_magic_absorb(&self, source: &DamageSource, mut damage: f32) -> f32 {
705 if source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_EFFECTS) {
706 return damage;
707 }
708
709 if !source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_RESISTANCE)
710 && let Some(resistance) = self.mob_effect(vanilla_mob_effects::RESISTANCE)
711 {
712 let absorb_value = (resistance.amplifier() + 1) * 5;
713 let absorb = 25 - absorb_value;
714 damage = (damage * absorb as f32 / 25.0).max(0.0);
715 }
716
717 if damage <= 0.0 {
718 return 0.0;
719 }
720 if source.is(&vanilla_damage_type_tags::DamageTypeTag::BYPASSES_ENCHANTMENTS) {
721 return damage;
722 }
723
724 let enchantment_armor = self.level().map_or(0.0, |world| {
725 enchantment_helper::get_damage_protection(&world, self, source)
726 });
727 if enchantment_armor > 0.0 {
728 damage = combat_rules::get_damage_after_magic_absorb(damage, enchantment_armor);
729 }
730 damage
731 }
732
733 fn actually_hurt(&self, world: &World, source: &DamageSource, amount: f32) {
735 if self.is_invulnerable_to(world, source) {
736 return;
737 }
738
739 let damage = self.get_damage_after_armor_absorb(source, amount);
740 let damage = self.get_damage_after_magic_absorb(source, damage);
741 let original_damage = damage;
742 let damage = (damage - self.get_absorption_amount()).max(0.0);
743 self.set_absorption_amount(self.get_absorption_amount() - (original_damage - damage));
744
745 if damage != 0.0 {
746 self.set_health(self.get_health() - damage);
747 self.set_absorption_amount(self.get_absorption_amount() - damage);
748 self.game_event(&vanilla_game_events::ENTITY_DAMAGE);
749 }
750 }
751
752 fn apply_damage_knockback(&self, source: &DamageSource) {
754 if source.is(&vanilla_damage_type_tags::DamageTypeTag::NO_KNOCKBACK) {
755 return;
756 }
757
758 let (xd, zd) = self.damage_knockback_direction(source);
759 self.knockback(DAMAGE_KNOCKBACK_POWER, xd, zd);
760 self.indicate_damage(xd, zd);
761 }
762
763 fn damage_knockback_direction(&self, source: &DamageSource) -> (f64, f64) {
765 if let Some(direct_entity_id) = source.direct_entity_id
766 && let Some(world) = self.level()
767 && let Some(direct_entity) = world.get_entity_by_id(direct_entity_id)
768 && let Some(projectile) = direct_entity.as_projectile()
769 && let Some(hurt_entity) = self.as_living_entity()
770 {
771 let (xd, zd) =
772 projectile.calculate_horizontal_hurt_knockback_direction(hurt_entity, source);
773 return (-xd, -zd);
774 }
775
776 let Some(source_position) = source.source_position else {
777 return (0.0, 0.0);
778 };
779
780 let position = self.position();
781 (
782 source_position.x - position.x,
783 source_position.z - position.z,
784 )
785 }
786
787 fn knockback(&self, mut power: f64, mut xd: f64, mut zd: f64) {
789 power *= 1.0 - self.knockback_resistance();
790 if power <= 0.0 {
791 return;
792 }
793
794 while xd * xd + zd * zd < KNOCKBACK_DIRECTION_EPSILON_SQ {
795 xd = (rand::random::<f64>() - rand::random::<f64>()) * 0.01;
796 zd = (rand::random::<f64>() - rand::random::<f64>()) * 0.01;
797 }
798
799 let old_velocity = self.velocity();
800 let delta_vector = DVec3::new(xd, 0.0, zd).normalize() * power;
801 self.set_velocity(DVec3::new(
802 old_velocity.x / 2.0 - delta_vector.x,
803 if self.on_ground() {
804 0.4_f64.min(old_velocity.y / 2.0 + power)
805 } else {
806 old_velocity.y
807 },
808 old_velocity.z / 2.0 - delta_vector.z,
809 ));
810 self.mark_velocity_sync();
811 }
812
813 fn knockback_resistance(&self) -> f64 {
815 self.attributes()
816 .lock()
817 .required_value(vanilla_attributes::KNOCKBACK_RESISTANCE)
818 }
819
820 fn indicate_damage(&self, _xd: f64, _zd: f64) {}
822
823 fn hurt_broadcast_chunk(&self) -> ChunkPos {
825 ChunkPos::from_entity_pos(self.position())
826 }
827
828 fn broadcast_damage_event(&self, world: &World, source: &DamageSource) {
830 world.broadcast_to_nearby(
831 self.hurt_broadcast_chunk(),
832 CDamageEvent {
833 entity_id: self.id(),
834 source_type_id: source.damage_type.id() as i32,
835 source_cause_id: source.causing_entity_id.map_or(0, |id| id + 1),
836 source_direct_id: source.direct_entity_id.map_or(0, |id| id + 1),
837 source_position: source.source_position,
838 },
839 None,
840 );
841 }
842
843 fn broadcast_hurt_animation(&self, world: &World) {
845 let (yaw, _) = self.rotation();
846 world.broadcast_to_nearby(
847 self.hurt_broadcast_chunk(),
848 CHurtAnimation {
849 entity_id: self.id(),
850 yaw,
851 },
852 None,
853 );
854 }
855
856 fn die(&self, source: &DamageSource) {
858 if self.is_removed() {
859 return;
860 }
861 if !self.living_base().mark_death_processed() {
862 return;
863 }
864
865 self.game_event(&vanilla_game_events::ENTITY_DIE);
866 self.drop_all_death_loot(source);
867 self.broadcast_entity_event(EntityStatus::Death);
868 self.set_pose(EntityPose::Dying);
869 }
870
871 fn should_drop_loot(&self, world: &World) -> bool {
873 !self.is_baby() && world.get_game_rule(&MOB_DROPS)
874 }
875
876 fn should_drop_experience(&self) -> bool {
878 !self.is_baby()
879 }
880
881 fn is_always_experience_dropper(&self) -> bool {
883 false
884 }
885
886 fn skip_drop_experience(&self) {
888 self.living_base().skip_drop_experience();
889 }
890
891 fn was_experience_consumed(&self) -> bool {
893 self.living_base().was_experience_consumed()
894 }
895
896 fn base_experience_reward(&self) -> i32 {
898 if let Some(animal) = self.as_animal() {
899 return animal.base_experience_reward_animal();
900 }
901
902 self.as_mob().map_or(0, Mob::base_experience_reward_mob)
903 }
904
905 fn experience_reward(&self, _world: &World, _killer_entity_id: Option<i32>) -> i32 {
907 self.base_experience_reward()
910 }
911
912 fn drop_all_death_loot(&self, source: &DamageSource) {
914 let Some(world) = self.level() else {
915 return;
916 };
917 if self.should_drop_loot(world.as_ref()) {
918 let killed_by_player = self.last_hurt_by_player_memory_time() > 0;
919 self.drop_from_loot_table(source, killed_by_player);
920 self.drop_custom_death_loot(source, killed_by_player);
921 if let Some(mob) = self.as_mob() {
922 mob.drop_custom_death_loot_mob(source, killed_by_player);
923 }
924 }
925 self.drop_experience(&world, source.causing_entity_id);
926 }
928
929 fn drop_experience(&self, world: &Arc<World>, killer_entity_id: Option<i32>) {
931 if self.was_experience_consumed() {
932 return;
933 }
934
935 let should_drop = self.is_always_experience_dropper()
936 || self.last_hurt_by_player_memory_time() > 0
937 && self.should_drop_experience()
938 && world.get_game_rule(&MOB_DROPS);
939 if !should_drop {
940 return;
941 }
942
943 let reward = self.experience_reward(world, killer_entity_id);
944 if reward > 0 {
945 ExperienceOrbEntity::award(world, self.position(), reward);
946 }
947 }
948
949 fn death_loot_table(&self) -> Option<LootTableRef> {
951 if let Some(mob) = self.as_mob()
952 && mob.has_custom_death_loot_table()
953 {
954 return mob.custom_death_loot_table();
955 }
956
957 let entity_type = self.entity_type();
958 let loot_key = Identifier::vanilla(format!("entities/{}", entity_type.key.path));
959 REGISTRY.loot_tables.by_key(&loot_key)
960 }
961
962 fn death_loot_table_seed(&self) -> i64 {
964 self.as_mob().map_or(0, Mob::death_loot_table_seed)
965 }
966
967 fn drop_from_loot_table(&self, source: &DamageSource, killed_by_player: bool) {
969 let Some(world) = self.level() else {
970 return;
971 };
972 let has_custom_death_loot_table =
973 self.as_mob().is_some_and(Mob::has_custom_death_loot_table);
974 let Some(loot_table) = self.death_loot_table() else {
975 if has_custom_death_loot_table && let Some(mob) = self.as_mob() {
976 mob.clear_custom_death_loot_table();
977 }
978 return;
979 };
980
981 let seed = self.death_loot_table_seed();
982 let drops = if seed == 0 {
983 let mut rng = rand::rng();
984 death_loot_items_with_rng(
985 self,
986 loot_table,
987 world.as_ref(),
988 source,
989 killed_by_player,
990 &mut rng,
991 )
992 } else {
993 let mut rng = StdRng::seed_from_u64(seed as u64);
994 death_loot_items_with_rng(
995 self,
996 loot_table,
997 world.as_ref(),
998 source,
999 killed_by_player,
1000 &mut rng,
1001 )
1002 };
1003
1004 if has_custom_death_loot_table && let Some(mob) = self.as_mob() {
1005 mob.clear_custom_death_loot_table();
1006 }
1007
1008 for item_stack in drops {
1009 self.spawn_at_location(item_stack, 0.0);
1010 }
1011 }
1012
1013 fn drop_custom_death_loot(&self, _source: &DamageSource, _killed_by_player: bool) {}
1015
1016 fn tick_death(&self) {
1018 let death_time = self.living_base().increment_death_time();
1019 if death_time >= DEATH_DURATION && !self.is_removed() {
1020 self.broadcast_entity_event(EntityStatus::Poof);
1021 self.set_removed(RemovalReason::Killed);
1022 }
1023 }
1024
1025 fn get_absorption_amount(&self) -> f32 {
1027 self.living_base().absorption_amount()
1028 }
1029
1030 fn set_absorption_amount(&self, amount: f32) {
1032 self.living_base().set_absorption_amount(amount);
1033 }
1034
1035 fn fall_damage_sound(&self, damage: i32) -> SoundEventRef {
1037 let (small, big) = self.fall_sounds();
1038 if damage > 4 { big } else { small }
1039 }
1040
1041 fn play_block_fall_sound(&self) {
1043 let Some(world) = self.level() else {
1044 return;
1045 };
1046 let position = self.position();
1047 let pos = BlockPos::new(
1048 position.x.floor() as i32,
1049 (position.y - f64::from(0.2_f32)).floor() as i32,
1050 position.z.floor() as i32,
1051 );
1052 let state = world.get_block_state(pos);
1053 if state.is_air() {
1054 return;
1055 }
1056
1057 let sound_type = state.get_block().config.sound_type;
1058 self.play_sound(
1059 sound_type.fall_sound,
1060 sound_type.volume * 0.5,
1061 sound_type.pitch * 0.75,
1062 );
1063 }
1064
1065 fn cause_living_fall_damage(
1067 &self,
1068 fall_distance: f64,
1069 damage_modifier: f32,
1070 source: &DamageSource,
1071 ) -> bool {
1072 let effective_fall_distance =
1073 if let Some(impact_pos) = self.living_base().current_impulse_impact_pos() {
1074 let effective_fall_distance = fall_distance.min(impact_pos.y - self.position().y);
1075 if effective_fall_distance <= 0.0 {
1076 self.reset_current_impulse_context();
1077 } else {
1078 self.try_reset_current_impulse_context();
1079 }
1080 effective_fall_distance
1081 } else {
1082 fall_distance
1083 };
1084
1085 if self.is_fall_damage_immune() {
1086 return false;
1087 }
1088
1089 self.propagate_fall_to_passengers(effective_fall_distance, damage_modifier, source);
1090
1091 let attributes = self.attributes().lock();
1092 let safe_fall_distance = attributes
1093 .get_value(vanilla_attributes::SAFE_FALL_DISTANCE)
1094 .unwrap_or(vanilla_attributes::SAFE_FALL_DISTANCE.default_value);
1095 let fall_damage_multiplier = attributes
1096 .get_value(vanilla_attributes::FALL_DAMAGE_MULTIPLIER)
1097 .unwrap_or(vanilla_attributes::FALL_DAMAGE_MULTIPLIER.default_value);
1098 drop(attributes);
1099
1100 let damage = LivingEntityBase::calculate_fall_damage(
1101 effective_fall_distance,
1102 damage_modifier,
1103 safe_fall_distance,
1104 fall_damage_multiplier,
1105 );
1106 if damage <= 0 {
1107 return false;
1108 }
1109
1110 self.reset_current_impulse_context();
1111 self.play_sound(self.fall_damage_sound(damage), 1.0, 1.0);
1112 self.play_block_fall_sound();
1113 if let Some(world) = self.level() {
1114 self.hurt(&world, source, damage as f32);
1115 }
1116 true
1117 }
1118
1119 fn get_armor_value(&self) -> i32 {
1121 self.attributes()
1122 .lock()
1123 .get_value(vanilla_attributes::ARMOR)
1124 .unwrap_or(0.0) as i32
1125 }
1126
1127 fn get_attribute_gravity(&self) -> f64 {
1129 self.attributes()
1130 .lock()
1131 .required_value(vanilla_attributes::GRAVITY)
1132 }
1133
1134 fn get_effective_gravity(&self) -> f64 {
1136 let gravity = self.get_gravity();
1137 if self.velocity().y <= 0.0 && self.has_mob_effect(vanilla_mob_effects::SLOW_FALLING) {
1138 gravity.min(0.01)
1139 } else {
1140 gravity
1141 }
1142 }
1143
1144 fn is_affected_by_potions(&self) -> bool {
1146 !self.is_dead_or_dying()
1147 }
1148
1149 fn default_can_be_affected(&self, effect: &MobEffectInstance) -> bool {
1151 if REGISTRY
1152 .entity_types
1153 .is_in_tag(self.entity_type(), &EntityTypeTag::IMMUNE_TO_INFESTED)
1154 {
1155 return effect.effect() != vanilla_mob_effects::INFESTED;
1156 }
1157 if REGISTRY
1158 .entity_types
1159 .is_in_tag(self.entity_type(), &EntityTypeTag::IMMUNE_TO_OOZING)
1160 {
1161 return effect.effect() != vanilla_mob_effects::OOZING;
1162 }
1163 if REGISTRY
1164 .entity_types
1165 .is_in_tag(self.entity_type(), &EntityTypeTag::IGNORES_POISON_AND_REGEN)
1166 {
1167 return effect.effect() != vanilla_mob_effects::REGENERATION
1168 && effect.effect() != vanilla_mob_effects::POISON;
1169 }
1170
1171 true
1172 }
1173
1174 fn can_be_affected(&self, effect: &MobEffectInstance) -> bool {
1178 self.default_can_be_affected(effect)
1179 }
1180
1181 fn has_mob_effect(&self, effect: MobEffectRef) -> bool {
1183 self.living_base().has_mob_effect(effect)
1184 }
1185
1186 fn mob_effect(&self, effect: MobEffectRef) -> Option<ActiveMobEffect> {
1188 self.living_base().mob_effect(effect)
1189 }
1190
1191 fn active_mob_effects(&self) -> Vec<ActiveMobEffect> {
1193 self.living_base().active_mob_effects()
1194 }
1195
1196 fn set_mob_effect(&self, effect: MobEffectRef, amplifier: i32) {
1198 self.add_mob_effect(MobEffectInstance::new(effect, amplifier));
1199 }
1200
1201 fn add_mob_effect(&self, effect: MobEffectInstance) -> bool {
1203 if !self.can_be_affected(&effect) {
1204 return false;
1205 }
1206 self.living_base().add_mob_effect(effect)
1207 }
1208
1209 fn set_mob_effect_active(&self, effect: MobEffectRef, active: bool) {
1211 if active {
1212 self.set_mob_effect(effect, 0);
1213 } else {
1214 self.remove_mob_effect(effect);
1215 }
1216 }
1217
1218 fn remove_mob_effect(&self, effect: MobEffectRef) -> bool {
1220 self.living_base().remove_mob_effect(effect)
1221 }
1222
1223 fn tick_mob_effects(&self) {
1225 let world = self.level();
1226 for effect in self.active_mob_effects() {
1227 if !effect.has_remaining_duration() {
1228 self.living_base().tick_mob_effect_duration(effect.effect());
1229 continue;
1230 }
1231
1232 if effect.should_apply_effect_tick_this_tick(self.tick_count())
1233 && world
1234 .as_deref()
1235 .is_some_and(|world| !effect.apply_effect_tick(world, self))
1236 {
1237 self.remove_mob_effect(effect.effect());
1238 continue;
1239 }
1240
1241 self.living_base().tick_mob_effect_duration(effect.effect());
1242 }
1243 }
1244
1245 fn has_water_breathing(&self) -> bool {
1247 self.has_mob_effect(vanilla_mob_effects::WATER_BREATHING)
1248 || self.has_mob_effect(vanilla_mob_effects::CONDUIT_POWER)
1249 || self.has_mob_effect(vanilla_mob_effects::BREATH_OF_THE_NAUTILUS)
1250 }
1251
1252 fn should_effects_refill_air_supply(&self) -> bool {
1254 !self.has_mob_effect(vanilla_mob_effects::BREATH_OF_THE_NAUTILUS)
1255 || self.has_mob_effect(vanilla_mob_effects::WATER_BREATHING)
1256 || self.has_mob_effect(vanilla_mob_effects::CONDUIT_POWER)
1257 }
1258
1259 fn can_breathe_underwater(&self) -> bool {
1261 self.entity_type().flags.can_breathe_underwater
1262 }
1263
1264 fn can_drown_in_water(&self) -> bool {
1266 if self.can_breathe_underwater() || self.has_water_breathing() {
1267 return false;
1268 }
1269
1270 !self
1271 .as_player()
1272 .is_some_and(|player| player.abilities.lock().invulnerable)
1273 }
1274
1275 fn is_eye_in_bubble_column(&self) -> bool {
1277 let Some(world) = self.level() else {
1278 return false;
1279 };
1280
1281 world
1282 .get_block_state(BlockPos::new(
1283 self.position().x.floor() as i32,
1284 self.get_eye_y().floor() as i32,
1285 self.position().z.floor() as i32,
1286 ))
1287 .get_block()
1288 == &vanilla_blocks::BUBBLE_COLUMN
1289 }
1290
1291 fn decrease_air_supply(&self, current_supply: i32) -> i32 {
1293 let oxygen_bonus = self
1294 .attributes()
1295 .lock()
1296 .get_value(vanilla_attributes::OXYGEN_BONUS)
1297 .unwrap_or(0.0);
1298 if oxygen_bonus > 0.0 && rand::random::<f64>() >= 1.0 / (oxygen_bonus + 1.0) {
1299 current_supply
1300 } else {
1301 current_supply - 1
1302 }
1303 }
1304
1305 fn increase_air_supply(&self, current_supply: i32) -> i32 {
1307 (current_supply + 4).min(self.max_air_supply())
1308 }
1309
1310 fn should_take_drowning_damage(&self) -> bool {
1312 self.air_supply() <= -20
1313 }
1314
1315 fn tick_living_air_supply(&self) {
1317 if !LivingEntity::is_alive(self) {
1318 return;
1319 }
1320
1321 let eye_in_water = self.is_eye_in_water() && !self.is_eye_in_bubble_column();
1322 if eye_in_water {
1323 if self.can_drown_in_water() {
1324 self.set_air_supply(self.decrease_air_supply(self.air_supply()));
1325 if self.should_take_drowning_damage() {
1326 self.set_air_supply(0);
1327 self.broadcast_entity_event(EntityStatus::DrownParticles);
1328 if let Some(world) = self.level() {
1329 self.hurt(
1330 &world,
1331 &DamageSource::environment(&vanilla_damage_types::DROWN),
1332 2.0,
1333 );
1334 }
1335 }
1336 } else if self.air_supply() < self.max_air_supply()
1337 && self.should_effects_refill_air_supply()
1338 {
1339 self.set_air_supply(self.increase_air_supply(self.air_supply()));
1340 }
1341
1342 if self
1343 .vehicle()
1344 .is_some_and(|vehicle| vehicle.dismounts_underwater())
1345 {
1346 self.stop_riding();
1347 }
1348 return;
1349 }
1350
1351 if self.air_supply() < self.max_air_supply() {
1352 self.set_air_supply(self.increase_air_supply(self.air_supply()));
1353 }
1354 }
1355
1356 fn is_in_wall(&self) -> bool {
1358 !self.is_sleeping() && Entity::is_in_wall(self)
1359 }
1360
1361 fn tick_in_wall_damage(&self) {
1363 if !LivingEntity::is_alive(self) || !LivingEntity::is_in_wall(self) {
1364 return;
1365 }
1366
1367 if let Some(world) = self.level() {
1368 self.hurt(
1369 &world,
1370 &DamageSource::environment(&vanilla_damage_types::IN_WALL),
1371 1.0,
1372 );
1373 }
1374 }
1375
1376 fn tick_living_environmental_damage(&self) {
1378 if !LivingEntity::is_alive(self) {
1379 return;
1380 }
1381
1382 if LivingEntity::is_in_wall(self) {
1383 self.tick_in_wall_damage();
1384 } else if self.as_player().is_some()
1385 && let Some(world) = self.level()
1386 {
1387 let border = world.world_border_snapshot();
1388 let position = self.position();
1389 if let Some(damage) =
1390 border.outside_damage_amount(position.x, position.z, self.bounding_box())
1391 {
1392 self.hurt(
1393 &world,
1394 &DamageSource::environment(&vanilla_damage_types::OUTSIDE_BORDER),
1395 damage,
1396 );
1397 }
1398 }
1399
1400 self.tick_living_air_supply();
1401 }
1402
1403 fn is_affected_by_fluids(&self) -> bool {
1405 true
1406 }
1407
1408 fn can_stand_on_fluid(&self, _fluid_state: FluidState) -> bool {
1410 false
1411 }
1412
1413 fn is_using_item(&self) -> bool {
1415 false
1416 }
1417
1418 fn is_blocking(&self) -> bool {
1420 false
1421 }
1422
1423 fn is_fall_flying(&self) -> bool {
1425 self.living_base().is_fall_flying()
1426 }
1427
1428 fn set_fall_flying(&self, fall_flying: bool) {
1430 self.set_shared_fall_flying(fall_flying);
1431 self.living_base().set_fall_flying(fall_flying);
1432 }
1433
1434 fn fall_flying_ticks(&self) -> i32 {
1436 self.living_base().fall_flying_ticks()
1437 }
1438
1439 fn with_equipment_slot(&self, slot: EquipmentSlot, visitor: &mut dyn FnMut(&ItemStack)) {
1441 let equipment = self.living_base().equipment().lock();
1442 visitor(equipment.get_ref(slot));
1443 }
1444
1445 fn is_holding(&self, predicate: &mut dyn FnMut(&ItemStack) -> bool) -> bool {
1447 let mut holding = false;
1448 self.with_equipment_slot(EquipmentSlot::MainHand, &mut |item_stack| {
1449 holding = predicate(item_stack);
1450 });
1451 if holding {
1452 return true;
1453 }
1454
1455 self.with_equipment_slot(EquipmentSlot::OffHand, &mut |item_stack| {
1456 holding = predicate(item_stack);
1457 });
1458 holding
1459 }
1460
1461 fn with_equipment_slot_mut(
1463 &self,
1464 slot: EquipmentSlot,
1465 visitor: &mut dyn FnMut(&mut ItemStack),
1466 ) {
1467 let mut equipment = self.living_base().equipment().lock();
1468 visitor(equipment.get_mut(slot));
1469 }
1470
1471 fn has_item_in_slot(&self, slot: EquipmentSlot) -> bool {
1473 let mut has_item = false;
1474 self.with_equipment_slot(slot, &mut |item_stack| {
1475 has_item = !item_stack.is_empty();
1476 });
1477 has_item
1478 }
1479
1480 fn can_use_slot(&self, _slot: EquipmentSlot) -> bool {
1482 true
1483 }
1484
1485 fn can_dispenser_equip_into_slot(&self, _slot: EquipmentSlot) -> bool {
1487 self.as_mob().is_none_or(Mob::can_pick_up_loot)
1488 }
1489
1490 fn can_equip_with_dispenser(&self, item_stack: &ItemStack) -> bool {
1492 if !Entity::is_alive(self) || self.is_spectator() {
1493 return false;
1494 }
1495
1496 let Some(equippable) = item_stack.get_equippable() else {
1497 return false;
1498 };
1499 if !equippable.dispensable {
1500 return false;
1501 }
1502
1503 let slot = equippable.slot;
1504 self.can_use_slot(slot)
1505 && equippable.can_be_equipped_by(self.entity_type())
1506 && !self.has_item_in_slot(slot)
1507 && self.can_dispenser_equip_into_slot(slot)
1508 }
1509
1510 fn is_equippable_in_slot(&self, item_stack: &ItemStack, slot: EquipmentSlot) -> bool {
1512 let Some(equippable) = item_stack.get_equippable() else {
1513 return slot == EquipmentSlot::MainHand && self.can_use_slot(EquipmentSlot::MainHand);
1514 };
1515
1516 slot == equippable.slot
1517 && self.can_use_slot(equippable.slot)
1518 && equippable.can_be_equipped_by(self.entity_type())
1519 }
1520
1521 fn equip_sound(&self, slot: EquipmentSlot, stack: &ItemStack) -> Option<SoundEventRef> {
1523 let equippable = stack.get_equippable()?;
1524 (slot == equippable.slot)
1525 .then(|| equippable.equip_sound.registry_ref())
1526 .flatten()
1527 }
1528
1529 fn interact_living_entity_with_equippable(
1531 &self,
1532 player: &Player,
1533 hand: InteractionHand,
1534 ) -> InteractionResult {
1535 let item_stack = {
1536 let inventory = player.inventory.lock();
1537 let item_stack = inventory.get_item_in_hand(hand);
1538 item_stack.copy_with_count(item_stack.count())
1539 };
1540 let Some(equippable) = item_stack.get_equippable() else {
1541 return InteractionResult::Pass;
1542 };
1543 if !equippable.equip_on_interact {
1544 return InteractionResult::Pass;
1545 }
1546
1547 let slot = equippable.slot;
1548 if !self.is_equippable_in_slot(&item_stack, slot) || !Entity::is_alive(self) {
1549 return InteractionResult::Pass;
1550 }
1551
1552 let equipped = {
1553 let mut equipment = self.living_base().equipment().lock();
1554 if !equipment.get_ref(slot).is_empty() {
1555 return InteractionResult::Pass;
1556 }
1557
1558 let mut inventory = player.inventory.lock();
1559 if !self.is_equippable_in_slot(inventory.get_item_in_hand(hand), slot) {
1560 return InteractionResult::Pass;
1561 }
1562
1563 let equipped = inventory.split_item_in_hand(hand, 1);
1564 if equipped.is_empty() {
1565 return InteractionResult::Pass;
1566 }
1567
1568 equipment.set(slot, equipped);
1569 equipment.get_ref(slot).copy_with_count(1)
1570 };
1571
1572 if let Some(sound) = self.equip_sound(slot, &equipped) {
1573 self.play_sound(sound, 1.0, 1.0);
1574 }
1575 if let Some(mob) = self.as_mob() {
1576 mob.set_guaranteed_drop(slot);
1577 }
1578 InteractionResult::Success
1580 }
1581
1582 fn refresh_equipment_attribute_modifiers(&self, slot: EquipmentSlot) {
1584 self.with_equipment_slot(slot, &mut |item_stack| {
1585 self.living_base()
1586 .refresh_equipment_attribute_modifiers(slot, item_stack);
1587 });
1588 }
1589
1590 fn detect_equipment_updates(&self) {
1592 let mut changes = self.living_base().collect_equipment_changes();
1593 if changes.is_empty() {
1594 return;
1595 }
1596
1597 for (slot, _, current) in &changes {
1598 self.living_base()
1599 .refresh_equipment_attribute_modifiers(*slot, current);
1600 }
1601
1602 let main_hand = changes
1603 .iter()
1604 .find(|(slot, _, _)| *slot == EquipmentSlot::MainHand);
1605 let offhand = changes
1606 .iter()
1607 .find(|(slot, _, _)| *slot == EquipmentSlot::OffHand);
1608 let hands_swapped = main_hand.zip(offhand).is_some_and(
1609 |((_, previous_main, current_main), (_, previous_off, current_off))| {
1610 ItemStack::matches(current_main, previous_off)
1611 && ItemStack::matches(current_off, previous_main)
1612 },
1613 );
1614
1615 if hands_swapped {
1616 if let Some(world) = self.level() {
1617 world.broadcast_to_entity_trackers(
1618 self.id(),
1619 CEntityEvent {
1620 entity_id: self.id(),
1621 event: EntityStatus::SwapHands,
1622 },
1623 None,
1624 );
1625 }
1626 changes.retain(|(slot, _, _)| {
1627 !matches!(slot, EquipmentSlot::MainHand | EquipmentSlot::OffHand)
1628 });
1629 }
1630
1631 self.living_base().queue_equipment_changes(
1632 changes
1633 .into_iter()
1634 .map(|(slot, _, current)| (slot, current)),
1635 );
1636 }
1637
1638 fn pack_living_equipment(&self) -> Vec<EquipmentSlotItem> {
1640 equipment_items_to_packet_items(self.living_base().equipment().lock().non_empty_items())
1641 }
1642
1643 fn drain_dirty_living_equipment(&self) -> Vec<EquipmentSlotItem> {
1645 equipment_items_to_packet_items(self.living_base().drain_equipment_changes())
1646 }
1647
1648 fn has_infinite_materials(&self) -> bool {
1650 false
1651 }
1652
1653 fn on_equipped_item_broken(&self, slot: EquipmentSlot) {
1655 let event = match slot {
1656 EquipmentSlot::MainHand => EntityStatus::MainhandBreak,
1657 EquipmentSlot::OffHand => EntityStatus::OffhandBreak,
1658 EquipmentSlot::Head => EntityStatus::HeadBreak,
1659 EquipmentSlot::Chest => EntityStatus::ChestBreak,
1660 EquipmentSlot::Legs => EntityStatus::LegsBreak,
1661 EquipmentSlot::Feet => EntityStatus::FeetBreak,
1662 EquipmentSlot::Body => EntityStatus::BodyBreak,
1663 EquipmentSlot::Saddle => EntityStatus::SaddleBreak,
1664 };
1665 self.broadcast_entity_event(event);
1666 self.refresh_equipment_attribute_modifiers(slot);
1667 }
1668
1669 fn default_living_can_freeze(&self) -> bool {
1675 for slot in EquipmentSlot::ALL {
1676 if !slot.is_armor() {
1677 continue;
1678 }
1679 let mut is_freeze_immune = false;
1680 self.with_equipment_slot(slot, &mut |item_stack| {
1681 is_freeze_immune = REGISTRY
1682 .items
1683 .is_in_tag(item_stack.item(), &ItemTag::FREEZE_IMMUNE_WEARABLES);
1684 });
1685
1686 if is_freeze_immune {
1687 return false;
1688 }
1689 }
1690
1691 self.default_can_freeze()
1692 }
1693
1694 fn is_on_non_air_block_for_frost(&self) -> bool {
1696 let Some(world) = self.level() else {
1697 return false;
1698 };
1699 let Some(pos) = self.on_pos_legacy() else {
1700 return false;
1701 };
1702
1703 world.get_block_state(pos).get_block() != &vanilla_blocks::AIR
1704 }
1705
1706 fn remove_frost(&self) {
1708 self.attributes().lock().remove_modifier(
1709 vanilla_attributes::MOVEMENT_SPEED,
1710 &SPEED_MODIFIER_POWDER_SNOW_ID,
1711 );
1712 }
1713
1714 fn try_add_frost(&self) {
1716 if !self.is_on_non_air_block_for_frost() || self.ticks_frozen() <= 0 {
1717 return;
1718 }
1719
1720 self.attributes().lock().add_modifier(
1721 vanilla_attributes::MOVEMENT_SPEED,
1722 AttributeModifier {
1723 id: SPEED_MODIFIER_POWDER_SNOW_ID,
1724 amount: f64::from(-0.05_f32 * self.percent_frozen()),
1725 operation: AttributeModifierOperation::AddValue,
1726 },
1727 false,
1728 );
1729 }
1730
1731 fn tick_freezing(&self) {
1733 if !self.is_in_powder_snow() || !self.can_freeze() {
1734 self.set_ticks_frozen((self.ticks_frozen() - 2).max(0));
1735 }
1736
1737 self.remove_frost();
1738 self.try_add_frost();
1739 if self.tick_count() % 40 == 0
1740 && self.is_fully_frozen()
1741 && self.can_freeze()
1742 && let Some(world) = self.level()
1743 {
1744 self.hurt(
1745 &world,
1746 &DamageSource::environment(&vanilla_damage_types::FREEZE),
1747 1.0,
1748 );
1749 }
1750 }
1751
1752 fn tick_living_entity(&self) {
1756 self.default_tick();
1757 self.living_base().decrement_invulnerable_time();
1758 self.tick_mob_effects();
1759 self.detect_equipment_updates();
1760
1761 if self.is_dead_or_dying() {
1762 self.tick_death();
1763 self.tick_living_state();
1764 return;
1765 }
1766
1767 if !self.is_removed() {
1768 self.ai_step();
1769 }
1770
1771 self.tick_living_state();
1772 }
1773
1774 fn tick_living_state(&self) {
1776 if let Some(mob) = self.as_mob() {
1777 mob.tick_body_rotation_control();
1778 }
1779 self.living_base()
1780 .tick_fall_flying_state(self.is_fall_flying());
1781 self.update_swing_time();
1782 self.refresh_dirty_attributes();
1783 self.living_base().tick_post_impulse_grace_time();
1784 self.living_base().tick_last_hurt_by_player_memory();
1785 self.living_base()
1786 .tick_living_combat_memory(self.tick_count());
1787 }
1788
1789 fn can_glide_using(&self, item_stack: &ItemStack, slot: EquipmentSlot) -> bool {
1791 let Some(equippable) = item_stack.get_equippable() else {
1792 return false;
1793 };
1794
1795 item_stack.has(GLIDER) && equippable.slot == slot && !item_stack.next_damage_will_break()
1796 }
1797
1798 fn can_glide_using_equipment_slot(&self, slot: EquipmentSlot) -> bool {
1800 let mut can_glide = false;
1801 self.with_equipment_slot(slot, &mut |item_stack| {
1802 can_glide = self.can_glide_using(item_stack, slot);
1803 });
1804 can_glide
1805 }
1806
1807 fn damage_random_glider(&self) {
1809 let mut slots_with_gliders = Vec::new();
1810 for slot in EquipmentSlot::ALL {
1811 if self.can_glide_using_equipment_slot(slot) {
1812 slots_with_gliders.push(slot);
1813 }
1814 }
1815
1816 let slot_count = slots_with_gliders.len();
1817 if slot_count == 0 {
1818 return;
1819 }
1820
1821 let slot_index = rand::random_range(0..slot_count);
1822 let slot_to_damage = slots_with_gliders[slot_index];
1823 let has_infinite_materials = self.has_infinite_materials();
1824 let mut item_broke = false;
1825 self.with_equipment_slot_mut(slot_to_damage, &mut |item_stack| {
1826 item_broke = item_stack.hurt_and_break(1, has_infinite_materials);
1827 });
1828 if item_broke {
1829 self.on_equipped_item_broken(slot_to_damage);
1830 }
1831 }
1832
1833 fn default_can_glide(&self) -> bool {
1835 !self.on_ground()
1836 && !self.is_passenger()
1837 && !self.has_mob_effect(vanilla_mob_effects::LEVITATION)
1838 && EquipmentSlot::ALL
1839 .iter()
1840 .any(|&slot| self.can_glide_using_equipment_slot(slot))
1841 }
1842
1843 fn can_glide(&self) -> bool {
1845 self.default_can_glide()
1846 }
1847
1848 fn start_fall_flying(&self) {
1850 self.set_fall_flying(true);
1851 }
1852
1853 fn try_to_start_fall_flying(&self) -> bool {
1855 if !self.is_fall_flying() && self.can_glide() && !self.is_in_water() {
1856 self.start_fall_flying();
1857 return true;
1858 }
1859
1860 false
1861 }
1862
1863 fn last_climbable_pos(&self) -> Option<BlockPos> {
1865 self.living_base().last_climbable_pos()
1866 }
1867
1868 fn set_last_climbable_pos(&self, pos: BlockPos) {
1870 self.living_base().set_last_climbable_pos(pos);
1871 }
1872
1873 fn default_living_on_climbable(&self) -> bool {
1875 if self.is_spectator() {
1876 return false;
1877 }
1878
1879 let pos = self.block_position();
1880 let Some(world) = self.level() else {
1881 return false;
1882 };
1883 let state = world.get_block_state(pos);
1884 let block = state.get_block();
1885
1886 if self.is_fall_flying() && block.has_tag(&BlockTag::CAN_GLIDE_THROUGH) {
1887 return false;
1888 }
1889
1890 let climbable = block.has_tag(&BlockTag::CLIMBABLE)
1891 || block.has_tag(&BlockTag::TRAPDOORS)
1892 && trapdoor_usable_as_ladder_state(state, world.get_block_state(pos.below()));
1893
1894 if climbable {
1895 self.set_last_climbable_pos(pos);
1896 }
1897
1898 climbable
1899 }
1900
1901 fn should_discard_friction(&self) -> bool {
1903 self.living_base().should_discard_friction()
1904 }
1905
1906 fn set_discard_friction(&self, discard_friction: bool) {
1908 self.living_base().set_discard_friction(discard_friction);
1909 }
1910
1911 fn is_jumping(&self) -> bool {
1913 self.living_base().is_jumping()
1914 }
1915
1916 fn set_jumping(&self, jumping: bool) {
1918 self.living_base().set_jumping(jumping);
1919 }
1920
1921 fn travel_input(&self) -> LivingTravelInput {
1923 self.living_base().travel_input()
1924 }
1925
1926 fn set_travel_input(&self, input: LivingTravelInput) {
1928 self.living_base().set_travel_input(input);
1929 }
1930
1931 fn apply_input(&self) {
1933 self.living_base().dampen_travel_input();
1934 }
1935
1936 fn no_jump_delay(&self) -> i32 {
1938 self.living_base().no_jump_delay()
1939 }
1940
1941 fn set_no_jump_delay(&self, ticks: i32) {
1943 self.living_base().set_no_jump_delay(ticks);
1944 }
1945
1946 fn tick_no_jump_delay(&self) {
1948 self.living_base().tick_no_jump_delay();
1949 }
1950
1951 fn default_is_immobile(&self) -> bool {
1953 self.is_dead_or_dying()
1954 }
1955
1956 fn is_immobile(&self) -> bool {
1958 self.default_is_immobile()
1959 }
1960
1961 fn apply_living_velocity_thresholds(&self) {
1963 let movement = self.velocity();
1964 let mut dx = movement.x;
1965 let mut dy = movement.y;
1966 let mut dz = movement.z;
1967
1968 if self.entity_type() == &vanilla_entities::PLAYER {
1969 if movement.x.mul_add(movement.x, movement.z * movement.z) < 9.0E-6 {
1970 dx = 0.0;
1971 dz = 0.0;
1972 }
1973 } else {
1974 if movement.x.abs() < 0.003 {
1975 dx = 0.0;
1976 }
1977 if movement.z.abs() < 0.003 {
1978 dz = 0.0;
1979 }
1980 }
1981
1982 if movement.y.abs() < 0.003 {
1983 dy = 0.0;
1984 }
1985
1986 self.set_velocity(DVec3::new(dx, dy, dz));
1987 }
1988
1989 fn server_ai_step(&self) {}
1991
1992 fn get_jump_boost_power(&self) -> f32 {
1994 self.mob_effect(vanilla_mob_effects::JUMP_BOOST)
1995 .map_or(0.0, |effect| 0.1 * (effect.amplifier() as f32 + 1.0))
1996 }
1997
1998 fn get_jump_power_with_multiplier(&self, multiplier: f32) -> f32 {
2000 let jump_strength =
2001 self.attributes()
2002 .lock()
2003 .get_value(vanilla_attributes::JUMP_STRENGTH)
2004 .unwrap_or(vanilla_attributes::JUMP_STRENGTH.default_value) as f32;
2005 jump_strength * multiplier * self.block_jump_factor() + self.get_jump_boost_power()
2006 }
2007
2008 fn get_jump_power(&self) -> f32 {
2010 self.get_jump_power_with_multiplier(1.0)
2011 }
2012
2013 fn default_jump_from_ground(&self) {
2015 let jump_power = self.get_jump_power();
2016 if jump_power <= 1.0E-5 {
2017 return;
2018 }
2019
2020 let movement = self.velocity();
2021 self.set_velocity(DVec3::new(
2022 movement.x,
2023 movement.y.max(f64::from(jump_power)),
2024 movement.z,
2025 ));
2026 if self.is_sprinting() {
2027 let angle = self.rotation().0.to_radians();
2028 self.set_velocity(
2029 self.velocity()
2030 + DVec3::new(
2031 f64::from(-angle.sin() * 0.2),
2032 0.0,
2033 f64::from(angle.cos() * 0.2),
2034 ),
2035 );
2036 }
2037
2038 self.mark_velocity_sync();
2039 }
2040
2041 fn jump_from_ground(&self) {
2043 self.default_jump_from_ground();
2044 }
2045
2046 fn go_down_in_water(&self) {
2048 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(-0.04_f32), 0.0));
2049 }
2050
2051 fn jump_in_liquid(&self, _fluid_tag: &Identifier) {
2053 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(0.04_f32), 0.0));
2054 }
2055
2056 fn handle_living_jump(&self) {
2058 if !self.is_jumping() || !self.is_affected_by_fluids() {
2059 self.set_no_jump_delay(0);
2060 return;
2061 }
2062
2063 let fluid_height = if self.is_in_lava() {
2064 self.fluid_contact().lava_height()
2065 } else {
2066 self.fluid_contact().water_height()
2067 };
2068 let in_water_and_has_fluid_height = self.is_in_water() && fluid_height > 0.0;
2069 let fluid_jump_threshold = self.get_fluid_jump_threshold();
2070 if !in_water_and_has_fluid_height
2071 || self.on_ground() && fluid_height <= fluid_jump_threshold
2072 {
2073 if !self.is_in_lava() || self.on_ground() && fluid_height <= fluid_jump_threshold {
2074 if (self.on_ground()
2075 || in_water_and_has_fluid_height && fluid_height <= fluid_jump_threshold)
2076 && self.no_jump_delay() == 0
2077 {
2078 self.jump_from_ground();
2079 self.set_no_jump_delay(10);
2080 }
2081 } else {
2082 self.jump_in_liquid(&vanilla_fluid_tags::FluidTag::LAVA);
2083 }
2084 } else {
2085 self.jump_in_liquid(&vanilla_fluid_tags::FluidTag::WATER);
2086 }
2087 }
2088
2089 fn tick_ridden(&self, _controller: &Player, _ridden_input: DVec3) {}
2091
2092 fn ridden_input(&self, _controller: &Player, self_input: DVec3) -> DVec3 {
2094 self_input
2095 }
2096
2097 fn ridden_speed(&self, _controller: &Player) -> f32 {
2099 self.get_speed()
2100 }
2101
2102 fn travel_ridden(&self, controller: &Player, self_input: DVec3) -> Option<MoveResult> {
2104 let ridden_input = self.ridden_input(controller, self_input);
2105 self.tick_ridden(controller, ridden_input);
2106 if self.can_simulate_movement() {
2107 self.set_speed(self.ridden_speed(controller));
2108 return self.travel(ridden_input);
2109 }
2110
2111 self.set_velocity(DVec3::ZERO);
2112 None
2113 }
2114
2115 fn default_ai_step(&self) -> Option<MoveResult> {
2120 self.tick_no_jump_delay();
2121 if !self.can_simulate_movement() {
2122 self.set_velocity(self.velocity() * 0.98);
2123 }
2124
2125 self.apply_living_velocity_thresholds();
2126 self.apply_input();
2127 if self.is_immobile() {
2128 self.set_jumping(false);
2129 let input = self.travel_input();
2130 self.set_travel_input(LivingTravelInput::new(0.0, input.vertical(), 0.0));
2131 } else if self.is_effective_ai() {
2132 self.server_ai_step();
2133 }
2134
2135 self.handle_living_jump();
2136
2137 if self.is_fall_flying() {
2138 self.update_fall_flying();
2139 }
2140
2141 if self.has_mob_effect(vanilla_mob_effects::SLOW_FALLING)
2142 || self.has_mob_effect(vanilla_mob_effects::LEVITATION)
2143 {
2144 self.reset_fall_distance();
2145 }
2146
2147 let input = self.travel_input();
2148 let input = DVec3::new(
2149 f64::from(input.sideways()),
2150 f64::from(input.vertical()),
2151 f64::from(input.forward()),
2152 );
2153 let result = if Entity::is_alive(self)
2154 && let Some(controller_entity) = self.controlling_passenger()
2155 && let Some(controller) = controller_entity.as_player()
2156 {
2157 self.travel_ridden(controller, input)
2158 } else if self.can_simulate_movement() && self.is_effective_ai() {
2159 self.travel(input)
2160 } else {
2161 None
2162 };
2163
2164 self.apply_effects_from_blocks();
2165 self.tick_freezing();
2166 self.push_entities();
2167 result
2168 }
2169
2170 fn ai_step(&self) -> Option<MoveResult> {
2172 self.default_ai_step()
2173 }
2174
2175 fn push_entities(&self) {
2177 let Some(world) = self.level() else {
2178 return;
2179 };
2180 if !world.tick_runs_normally() {
2181 return;
2182 }
2183
2184 let pusher = self.as_entity_event_source();
2185 let pushable_entities = world.get_pushable_entities(pusher, &self.bounding_box());
2186 if pushable_entities.is_empty() {
2187 return;
2188 }
2189
2190 self.apply_entity_cramming_damage(&world, &pushable_entities);
2191
2192 for entity in pushable_entities {
2193 entity.push_entity(pusher);
2194 }
2195 }
2196
2197 fn apply_entity_cramming_damage(&self, world: &World, pushable_entities: &[SharedEntity]) {
2199 let max_cramming = world.get_game_rule(&MAX_ENTITY_CRAMMING);
2200
2201 if max_cramming <= 0 || pushable_entities.len() <= (max_cramming - 1) as usize {
2202 return;
2203 }
2204
2205 let random_roll = rand::random_range(0..4);
2206 let non_passenger_count = pushable_entities
2207 .iter()
2208 .filter(|entity| !entity.is_passenger())
2209 .count();
2210
2211 if should_apply_entity_cramming_damage(
2212 max_cramming,
2213 pushable_entities.len(),
2214 non_passenger_count,
2215 random_roll,
2216 ) {
2217 self.hurt(
2218 world,
2219 &DamageSource::environment(&vanilla_damage_types::CRAMMING),
2220 6.0,
2221 );
2222 }
2223 }
2224
2225 fn is_suppressing_sliding_down_ladder(&self) -> bool {
2227 self.is_suppressing_bounce()
2228 }
2229
2230 fn levitation_travel_y_delta(&self, movement_y: f64) -> Option<f64> {
2232 self.mob_effect(vanilla_mob_effects::LEVITATION)
2233 .map(|effect| (0.05 * f64::from(effect.amplifier() + 1) - movement_y) * 0.2)
2234 }
2235
2236 fn should_travel_in_fluid(&self, fluid_state: FluidState) -> bool {
2238 (self.is_in_water() || self.is_in_lava())
2239 && self.is_affected_by_fluids()
2240 && !self.can_stand_on_fluid(fluid_state)
2241 }
2242
2243 fn get_water_slow_down(&self) -> f32 {
2245 0.8
2246 }
2247
2248 fn water_movement_efficiency(&self) -> f32 {
2250 self.attributes()
2251 .lock()
2252 .get_value(vanilla_attributes::WATER_MOVEMENT_EFFICIENCY)
2253 .unwrap_or(0.0) as f32
2254 }
2255
2256 fn has_dolphins_grace(&self) -> bool {
2258 self.has_mob_effect(vanilla_mob_effects::DOLPHINS_GRACE)
2259 }
2260
2261 fn get_flying_speed(&self) -> f32 {
2263 if self
2264 .controlling_passenger()
2265 .is_some_and(|passenger| passenger.entity_type() == &vanilla_entities::PLAYER)
2266 {
2267 self.get_speed() * 0.1
2268 } else {
2269 0.02
2270 }
2271 }
2272
2273 fn get_friction_influenced_speed(&self, block_friction: f32) -> f32 {
2275 if self.on_ground() {
2276 self.get_speed() * (0.216_000_02 / (block_friction * block_friction * block_friction))
2277 } else {
2278 self.get_flying_speed()
2279 }
2280 }
2281
2282 fn air_travel_vertical_friction(&self, _horizontal_friction: f32) -> f32 {
2284 0.98
2286 }
2287
2288 fn handle_on_climbable(&self, movement: DVec3) -> DVec3 {
2290 if !self.on_climbable() {
2291 return movement;
2292 }
2293
2294 self.reset_fall_distance();
2295 let Some(world) = self.level() else {
2296 return movement;
2297 };
2298 let block_state = self.in_block_state(&world);
2299 let mut y = movement.y.max(-0.15);
2300 if y < 0.0
2301 && block_state.get_block() != &vanilla_blocks::SCAFFOLDING
2302 && self.is_suppressing_sliding_down_ladder()
2303 && self.entity_type() == &vanilla_entities::PLAYER
2304 {
2305 y = 0.0;
2306 }
2307
2308 DVec3::new(
2309 movement.x.clamp(-0.15, 0.15),
2310 y,
2311 movement.z.clamp(-0.15, 0.15),
2312 )
2313 }
2314
2315 fn apply_living_travel_gravity(&self) {
2317 let gravity = self.get_effective_gravity();
2318 if gravity != 0.0 {
2319 let mut velocity = self.velocity();
2320 velocity.y -= gravity;
2321 self.set_velocity(velocity);
2322 }
2323 }
2324
2325 fn handle_relative_friction_and_calculate_movement(
2327 &self,
2328 input: DVec3,
2329 block_friction: f32,
2330 ) -> Option<(DVec3, MoveResult)> {
2331 self.move_relative(self.get_friction_influenced_speed(block_friction), input);
2332 self.set_velocity(self.handle_on_climbable(self.velocity()));
2333 let result = self.move_entity(MoverType::SelfMovement, self.velocity())?;
2334 let mut movement = self.velocity();
2335 if (result.horizontal_collision || self.is_jumping())
2336 && (self.on_climbable()
2337 || self.was_in_powder_snow()
2338 && PowderSnowBlock::can_entity_walk_on_powder_snow(self))
2339 {
2340 movement.y = 0.2;
2341 }
2342
2343 Some((movement, result))
2344 }
2345
2346 fn travel_in_air(&self, input: DVec3) -> Option<MoveResult> {
2348 let world = self.level()?;
2349 let pos_below = self.block_pos_below_that_affects_movement()?;
2350 let block_friction = if self.on_ground() {
2351 world.get_block_state(pos_below).get_block().config.friction
2352 } else {
2353 1.0
2354 };
2355 let horizontal_friction = block_friction * 0.91;
2356 let (movement, result) =
2357 self.handle_relative_friction_and_calculate_movement(input, block_friction)?;
2358 let movement_y = if let Some(levitation_y) = self.levitation_travel_y_delta(movement.y) {
2359 movement.y + levitation_y
2360 } else {
2361 movement.y - self.get_effective_gravity()
2362 };
2363
2364 if self.should_discard_friction() {
2365 self.set_velocity(DVec3::new(movement.x, movement_y, movement.z));
2366 } else {
2367 let vertical_friction = self.air_travel_vertical_friction(horizontal_friction);
2368 self.set_velocity(DVec3::new(
2369 movement.x * f64::from(horizontal_friction),
2370 movement_y * f64::from(vertical_friction),
2371 movement.z * f64::from(horizontal_friction),
2372 ));
2373 }
2374
2375 Some(result)
2376 }
2377
2378 fn get_fluid_falling_adjusted_movement(
2380 &self,
2381 base_gravity: f64,
2382 is_falling: bool,
2383 movement: DVec3,
2384 ) -> DVec3 {
2385 if base_gravity == 0.0 || self.is_sprinting() {
2386 return movement;
2387 }
2388
2389 let y = if is_falling
2390 && (movement.y - 0.005).abs() >= 0.003
2391 && (movement.y - base_gravity / 16.0).abs() < 0.003
2392 {
2393 -0.003
2394 } else {
2395 movement.y - base_gravity / 16.0
2396 };
2397
2398 DVec3::new(movement.x, y, movement.z)
2399 }
2400
2401 fn jump_out_of_fluid(&self, old_y: f64) {
2403 if !self.horizontal_collision() {
2404 return;
2405 }
2406
2407 let movement = self.velocity();
2408 let target_delta = DVec3::new(
2409 movement.x,
2410 movement.y + f64::from(0.6_f32) - self.position().y + old_y,
2411 movement.z,
2412 );
2413 if self.is_free(target_delta) {
2414 self.set_velocity(DVec3::new(movement.x, f64::from(0.3_f32), movement.z));
2415 }
2416 }
2417
2418 fn float_in_water_while_ridden(&self) {
2420 if !REGISTRY
2421 .entity_types
2422 .is_in_tag(self.entity_type(), &EntityTypeTag::CAN_FLOAT_WHILE_RIDDEN)
2423 {
2424 return;
2425 }
2426 if !self.is_vehicle()
2427 || self.fluid_contact().water_height() <= self.get_fluid_jump_threshold()
2428 {
2429 return;
2430 }
2431
2432 self.set_velocity(self.velocity() + DVec3::new(0.0, f64::from(0.04_f32), 0.0));
2433 }
2434
2435 fn travel_in_water(
2437 &self,
2438 input: DVec3,
2439 base_gravity: f64,
2440 is_falling: bool,
2441 old_y: f64,
2442 ) -> Option<MoveResult> {
2443 let mut slow_down = if self.is_sprinting() {
2444 0.9
2445 } else {
2446 self.get_water_slow_down()
2447 };
2448 let mut speed = 0.02;
2449 let mut water_movement_efficiency = self.water_movement_efficiency();
2450 if !self.on_ground() {
2451 water_movement_efficiency *= 0.5;
2452 }
2453
2454 if water_movement_efficiency > 0.0 {
2455 slow_down += (0.546_000_06 - slow_down) * water_movement_efficiency;
2456 speed += (self.get_speed() - speed) * water_movement_efficiency;
2457 }
2458
2459 if self.has_dolphins_grace() {
2460 slow_down = 0.96;
2461 }
2462
2463 self.move_relative(speed, input);
2464 let result = self.move_entity(MoverType::SelfMovement, self.velocity())?;
2465 let mut movement = self.velocity();
2466 if result.horizontal_collision && self.on_climbable() {
2467 movement.y = 0.2;
2468 }
2469
2470 movement = DVec3::new(
2471 movement.x * f64::from(slow_down),
2472 movement.y * f64::from(0.8_f32),
2473 movement.z * f64::from(slow_down),
2474 );
2475 self.set_velocity(self.get_fluid_falling_adjusted_movement(
2476 base_gravity,
2477 is_falling,
2478 movement,
2479 ));
2480 self.jump_out_of_fluid(old_y);
2481
2482 Some(result)
2483 }
2484
2485 fn travel_in_lava(
2487 &self,
2488 input: DVec3,
2489 base_gravity: f64,
2490 is_falling: bool,
2491 old_y: f64,
2492 ) -> Option<MoveResult> {
2493 self.move_relative(0.02, input);
2494 let result = self.move_entity(MoverType::SelfMovement, self.velocity())?;
2495 if self.fluid_contact().lava_height() <= self.get_fluid_jump_threshold() {
2496 let movement = self.velocity();
2497 self.set_velocity(DVec3::new(
2498 movement.x * 0.5,
2499 movement.y * f64::from(0.8_f32),
2500 movement.z * 0.5,
2501 ));
2502 self.set_velocity(self.get_fluid_falling_adjusted_movement(
2503 base_gravity,
2504 is_falling,
2505 self.velocity(),
2506 ));
2507 } else {
2508 self.set_velocity(self.velocity() * 0.5);
2509 }
2510
2511 if base_gravity != 0.0 {
2512 self.set_velocity(self.velocity() + DVec3::new(0.0, -base_gravity / 4.0, 0.0));
2513 }
2514
2515 self.jump_out_of_fluid(old_y);
2516
2517 Some(result)
2518 }
2519
2520 fn travel_in_fluid(&self, input: DVec3) -> Option<MoveResult> {
2522 let is_falling = self.velocity().y <= 0.0;
2523 let old_y = self.position().y;
2524 let base_gravity = self.get_effective_gravity();
2525 if self.is_in_water() {
2526 let result = self.travel_in_water(input, base_gravity, is_falling, old_y);
2527 self.float_in_water_while_ridden();
2528 return result;
2529 }
2530
2531 self.travel_in_lava(input, base_gravity, is_falling, old_y)
2532 }
2533
2534 fn update_fall_flying(&self) {
2536 self.check_fall_distance_accumulation();
2537 if self.can_glide() {
2538 if let Some(free_fall_interval) =
2539 fall_flying_free_fall_interval(self.fall_flying_ticks())
2540 {
2541 if free_fall_interval % 2 == 0 {
2542 self.damage_random_glider();
2543 }
2544 if let Some(world) = self.level() {
2545 world.game_event_at(
2546 &vanilla_game_events::ELYTRA_GLIDE,
2547 self.position(),
2548 &GameEventContext::new(Some(self.as_entity_event_source()), None),
2549 );
2550 }
2551 }
2552 } else {
2553 self.set_fall_flying(false);
2554 }
2555 }
2556
2557 fn update_fall_flying_movement(&self, mut movement: DVec3) -> DVec3 {
2559 let look_angle = self.look_angle();
2560 let pitch_radians = self.rotation().1.to_radians();
2561 let look_horizontal_length = horizontal_distance(look_angle);
2562 let move_horizontal_length = horizontal_distance(movement);
2563 let gravity = self.get_effective_gravity();
2564 let lift_force = f64::from(pitch_radians).cos().powi(2);
2565 movement.y += gravity * (-1.0 + lift_force * 0.75);
2566
2567 if movement.y < 0.0 && look_horizontal_length > 0.0 {
2568 let convert = movement.y * -0.1 * lift_force;
2569 movement += DVec3::new(
2570 look_angle.x * convert / look_horizontal_length,
2571 convert,
2572 look_angle.z * convert / look_horizontal_length,
2573 );
2574 }
2575
2576 if pitch_radians < 0.0 && look_horizontal_length > 0.0 {
2577 let convert = move_horizontal_length * -f64::from(pitch_radians.sin()) * 0.04;
2578 movement += DVec3::new(
2579 -look_angle.x * convert / look_horizontal_length,
2580 convert * 3.2,
2581 -look_angle.z * convert / look_horizontal_length,
2582 );
2583 }
2584
2585 if look_horizontal_length > 0.0 {
2586 movement += DVec3::new(
2587 (look_angle.x / look_horizontal_length * move_horizontal_length - movement.x) * 0.1,
2588 0.0,
2589 (look_angle.z / look_horizontal_length * move_horizontal_length - movement.z) * 0.1,
2590 );
2591 }
2592
2593 DVec3::new(
2594 movement.x * f64::from(0.99_f32),
2595 movement.y * f64::from(0.98_f32),
2596 movement.z * f64::from(0.99_f32),
2597 )
2598 }
2599
2600 fn stop_fall_flying(&self) {
2602 self.set_fall_flying(true);
2603 self.set_fall_flying(false);
2604 }
2605
2606 fn handle_fall_flying_collisions(
2608 &self,
2609 previous_horizontal_speed: f64,
2610 new_horizontal_speed: f64,
2611 ) {
2612 if !self.horizontal_collision() {
2613 return;
2614 }
2615
2616 let damage = fall_flying_collision_damage(previous_horizontal_speed, new_horizontal_speed);
2617 if damage <= 0.0 {
2618 return;
2619 }
2620
2621 self.play_sound(self.fall_damage_sound(damage as i32), 1.0, 1.0);
2622 if let Some(world) = self.level() {
2623 self.hurt(
2624 &world,
2625 &DamageSource::environment(&vanilla_damage_types::FLY_INTO_WALL),
2626 damage,
2627 );
2628 }
2629 }
2630
2631 fn travel_fall_flying(&self, input: DVec3) -> Option<MoveResult> {
2633 if self.on_climbable() {
2634 let result = self.travel_in_air(input);
2635 self.stop_fall_flying();
2636 return result;
2637 }
2638
2639 let previous_movement = self.velocity();
2640 let previous_horizontal_speed = horizontal_distance(previous_movement);
2641 self.set_velocity(self.update_fall_flying_movement(previous_movement));
2642 let result = self.move_entity(MoverType::SelfMovement, self.velocity());
2643 let new_horizontal_speed = horizontal_distance(self.velocity());
2644 self.handle_fall_flying_collisions(previous_horizontal_speed, new_horizontal_speed);
2645 result
2646 }
2647
2648 fn default_travel(&self, input: DVec3) -> Option<MoveResult> {
2650 let world = self.level()?;
2651 let fluid_state = get_fluid_state(&world, self.block_position());
2652 if self.should_travel_in_fluid(fluid_state) {
2653 return self.travel_in_fluid(input);
2654 }
2655 if self.is_fall_flying() {
2656 return self.travel_fall_flying(input);
2657 }
2658
2659 self.travel_in_air(input)
2660 }
2661
2662 fn travel(&self, input: DVec3) -> Option<MoveResult> {
2664 self.default_travel(input)
2665 }
2666
2667 fn sleeping_pos(&self) -> Option<BlockPos> {
2669 self.living_base().sleeping_pos()
2670 }
2671
2672 fn set_sleeping_pos(&self, bed_position: BlockPos) {
2674 self.living_base().set_sleeping_pos(bed_position);
2675 }
2676
2677 fn clear_sleeping_pos(&self) {
2679 self.living_base().clear_sleeping_pos();
2680 }
2681
2682 fn is_sleeping(&self) -> bool {
2684 self.sleeping_pos().is_some()
2685 }
2686
2687 fn stop_sleeping(&self) {
2689 self.clear_sleeping_pos();
2690 }
2691
2692 fn is_sprinting(&self) -> bool {
2694 self.living_base().is_sprinting()
2695 }
2696
2697 fn set_sprinting(&self, sprinting: bool) {
2699 self.set_shared_sprinting(sprinting);
2700 self.living_base().set_sprinting(sprinting);
2701 }
2702
2703 fn get_speed(&self) -> f32 {
2705 self.living_base().speed()
2706 }
2707
2708 fn set_speed(&self, speed: f32) {
2710 self.living_base().set_speed(speed);
2711 }
2712
2713 fn apply_post_impulse_grace_time(&self, ticks: i32) {
2715 self.living_base().apply_post_impulse_grace_time(ticks);
2716 }
2717
2718 fn set_ignore_fall_damage_from_current_impulse(
2720 &self,
2721 ignore_fall_damage: bool,
2722 new_impulse_impact_pos: DVec3,
2723 ) {
2724 self.living_base()
2725 .set_ignore_fall_damage_from_current_impulse(
2726 ignore_fall_damage,
2727 new_impulse_impact_pos,
2728 );
2729 }
2730
2731 fn is_ignoring_fall_damage_from_current_impulse(&self) -> bool {
2733 self.living_base()
2734 .is_ignoring_fall_damage_from_current_impulse()
2735 }
2736
2737 fn current_impulse_impact_pos(&self) -> Option<DVec3> {
2739 self.living_base().current_impulse_impact_pos()
2740 }
2741
2742 fn try_reset_current_impulse_context(&self) {
2744 self.living_base().try_reset_current_impulse_context();
2745 }
2746
2747 fn reset_current_impulse_context(&self) {
2749 self.living_base().reset_current_impulse_context();
2750 }
2751
2752 fn is_in_post_impulse_grace_time(&self) -> bool {
2754 self.living_base().is_in_post_impulse_grace_time()
2755 }
2756
2757 fn tick_post_impulse_grace_time(&self) {
2759 self.living_base().tick_post_impulse_grace_time();
2760 }
2761
2762 fn refresh_dirty_attributes(&self) {
2764 let dirty = self.attributes().lock().drain_dirty_updates();
2765 for attr in dirty {
2766 if attr.key == vanilla_attributes::MAX_HEALTH.key {
2767 let max = self.get_max_health();
2768 if self.get_health() > max {
2769 self.set_health(max);
2770 }
2771 } else if attr.key == vanilla_attributes::MAX_ABSORPTION.key {
2772 let max = self
2773 .attributes()
2774 .lock()
2775 .get_value(vanilla_attributes::MAX_ABSORPTION)
2776 .unwrap_or(0.0) as f32;
2777 if self.get_absorption_amount() > max {
2778 self.set_absorption_amount(max);
2779 }
2780 } else if attr.key == vanilla_attributes::SCALE.key {
2781 self.refresh_dimensions();
2782 }
2783 }
2785 }
2786}
2787
2788fn death_loot_items_with_rng<R: rand::Rng, E: LivingEntity + ?Sized>(
2789 entity: &E,
2790 loot_table: LootTableRef,
2791 world: &World,
2792 source: &DamageSource,
2793 killed_by_player: bool,
2794 rng: &mut R,
2795) -> Vec<ItemStack> {
2796 let causing_entity = source
2797 .causing_entity_id
2798 .and_then(|entity_id| world.get_entity_by_id(entity_id));
2799 let direct_entity = source
2800 .direct_entity_id
2801 .and_then(|entity_id| world.get_entity_by_id(entity_id));
2802 let last_damage_player = if killed_by_player {
2803 entity
2804 .last_hurt_by_player_uuid()
2805 .and_then(|uuid| world.get_entity_by_uuid(&uuid))
2806 } else {
2807 None
2808 };
2809
2810 let position = entity.position();
2811 let this_entity = living_entity_loot_ref(entity);
2812 let causing_entity = causing_entity.as_deref().map(entity_loot_ref);
2813 let direct_entity = direct_entity.as_deref().map(entity_loot_ref);
2814 let last_damage_player = last_damage_player.as_deref().map(entity_loot_ref);
2815 let damage_source = DamageSourceInfo {
2816 damage_type: Some(&source.damage_type.key),
2817 tags: &[],
2818 is_direct: source.is_direct(),
2819 };
2820
2821 let mut context = LootContext::new(rng)
2822 .with_origin(position.x, position.y, position.z)
2823 .with_game_time(world.game_time())
2824 .with_killed_by_player(killed_by_player)
2825 .with_this_entity(this_entity)
2826 .with_damage_source(damage_source);
2827 if let Some(entity) = causing_entity {
2828 context = context.with_killer_entity(entity);
2829 }
2830 if let Some(entity) = direct_entity {
2831 context = context.with_direct_killer_entity(entity);
2832 }
2833 if let Some(entity) = last_damage_player {
2834 context = context.with_last_damage_player(entity);
2835 }
2836
2837 loot_table.get_random_items(&mut context)
2838}
2839
2840fn living_entity_loot_ref<E: LivingEntity + ?Sized>(entity: &E) -> EntityRef<'_> {
2841 EntityRef {
2842 entity_type: Some(&entity.entity_type().key),
2843 flags: EntityRefFlags {
2844 is_on_fire: entity.is_on_fire(),
2845 is_sneaking: entity.is_crouching(),
2846 is_sprinting: entity.is_sprinting(),
2847 is_swimming: entity.is_swimming(),
2848 is_baby: entity.is_baby(),
2849 },
2850 equipment: None,
2852 custom_name: None,
2853 }
2854}