1use std::{array, mem, sync::Arc};
9
10use glam::DVec3;
11use rustc_hash::FxHashMap;
12use simdnbt::owned::{NbtCompound, NbtTag};
13use steel_protocol::packets::game::{CRemoveMobEffect, CUpdateMobEffect, MobEffectPacketFlags};
14use steel_registry::RegistryEntry;
15use steel_registry::attribute::AttributeRef;
16use steel_registry::entity_data::ParticleList;
17use steel_registry::entity_type::EntityTypeRef;
18use steel_registry::item_stack::ItemStack;
19use steel_registry::mob_effect::MobEffectRef;
20use steel_registry::vanilla_attributes;
21use steel_registry::vanilla_entity_data::VanillaLivingEntityData;
22use steel_registry::{vanilla_damage_types, vanilla_mob_effects};
23use steel_utils::locks::{IntoShared, Shared, SyncMutex};
24use steel_utils::types::InteractionHand;
25use steel_utils::{BlockPos, Identifier};
26use uuid::Uuid;
27
28use crate::entity::attribute::{AttributeMap, AttributeModifier, AttributeModifierOperation};
29use crate::entity::damage::DamageSource;
30use crate::entity::{LivingEntity, SharedEntity, WeakEntity};
31use crate::inventory::equipment::{EntityEquipment, EquipmentSlot, OwnedEntityEquipment};
32use crate::world::World;
33
34pub const DEATH_DURATION: i32 = 20;
36pub const DEFAULT_SWING_DURATION: i32 = 6;
38const INFINITE_EFFECT_DURATION: i32 = -1;
39const MIN_EFFECT_AMPLIFIER: i32 = 0;
40const MAX_EFFECT_AMPLIFIER: i32 = 255;
41const SPRINT_SPEED_MODIFIER_AMOUNT: f64 = 0.3;
42const POST_IMPULSE_GRACE_TICKS: i32 = 40;
43
44#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct MobEffectInstance {
50 effect: MobEffectRef,
51 duration: i32,
52 amplifier: i32,
53 ambient: bool,
54 visible: bool,
55 show_icon: bool,
56 hidden_effect: Option<Box<MobEffectInstance>>,
57}
58
59pub type ActiveMobEffect = MobEffectInstance;
61
62impl MobEffectInstance {
63 #[must_use]
65 pub const fn new(effect: MobEffectRef, amplifier: i32) -> Self {
66 Self::with_duration(effect, INFINITE_EFFECT_DURATION, amplifier)
67 }
68
69 #[must_use]
71 pub const fn with_duration(effect: MobEffectRef, duration: i32, amplifier: i32) -> Self {
72 Self {
73 effect,
74 duration,
75 amplifier: clamp_effect_amplifier(amplifier),
76 ambient: false,
77 visible: true,
78 show_icon: true,
79 hidden_effect: None,
80 }
81 }
82
83 #[must_use]
85 pub const fn with_ambient(mut self, ambient: bool) -> Self {
86 self.ambient = ambient;
87 self
88 }
89
90 #[must_use]
92 pub const fn with_visible(mut self, visible: bool) -> Self {
93 self.visible = visible;
94 self
95 }
96
97 #[must_use]
99 pub const fn with_show_icon(mut self, show_icon: bool) -> Self {
100 self.show_icon = show_icon;
101 self
102 }
103
104 #[must_use]
106 pub const fn effect(&self) -> MobEffectRef {
107 self.effect
108 }
109
110 #[must_use]
112 pub const fn duration(&self) -> i32 {
113 self.duration
114 }
115
116 #[must_use]
118 pub const fn amplifier(&self) -> i32 {
119 self.amplifier
120 }
121
122 #[must_use]
124 pub const fn is_ambient(&self) -> bool {
125 self.ambient
126 }
127
128 #[must_use]
130 pub const fn is_visible(&self) -> bool {
131 self.visible
132 }
133
134 #[must_use]
136 pub const fn show_icon(&self) -> bool {
137 self.show_icon
138 }
139
140 #[must_use]
142 pub const fn is_infinite_duration(&self) -> bool {
143 self.duration == INFINITE_EFFECT_DURATION
144 }
145
146 #[must_use]
148 pub(crate) fn to_vanilla_nbt(&self) -> NbtCompound {
149 let mut nbt = self.details_to_vanilla_nbt();
150 nbt.insert("id", self.effect.key.to_string());
151 nbt
152 }
153
154 fn details_to_vanilla_nbt(&self) -> NbtCompound {
155 let mut nbt = NbtCompound::new();
156 if self.amplifier != 0 {
157 nbt.insert("amplifier", NbtTag::Byte(self.amplifier as i8));
158 }
159 if self.duration != 0 {
160 nbt.insert("duration", self.duration);
161 }
162 if self.ambient {
163 nbt.insert("ambient", NbtTag::Byte(1));
164 }
165 if !self.visible {
166 nbt.insert("show_particles", NbtTag::Byte(0));
167 }
168 nbt.insert("show_icon", NbtTag::Byte(i8::from(self.show_icon)));
169 if let Some(hidden_effect) = self.hidden_effect.as_deref() {
170 nbt.insert(
171 "hidden_effect",
172 NbtTag::Compound(hidden_effect.details_to_vanilla_nbt()),
173 );
174 }
175 nbt
176 }
177
178 #[must_use]
179 pub(crate) const fn has_remaining_duration(&self) -> bool {
180 self.is_infinite_duration() || self.duration > 0
181 }
182
183 #[must_use]
184 pub(crate) fn should_apply_effect_tick_this_tick(&self, entity_tick_count: i32) -> bool {
185 let tick_count = if self.is_infinite_duration() {
186 entity_tick_count
187 } else {
188 self.duration
189 };
190
191 if self.effect == vanilla_mob_effects::WITHER {
192 let interval = 40_i32.wrapping_shr(self.amplifier as u32);
193 return interval <= 0 || tick_count % interval == 0;
194 }
195
196 false
198 }
199
200 pub(crate) fn apply_effect_tick<E: LivingEntity + ?Sized>(
201 &self,
202 world: &World,
203 entity: &E,
204 ) -> bool {
205 if self.effect == vanilla_mob_effects::WITHER {
206 entity.hurt(
207 world,
208 &DamageSource::environment(&vanilla_damage_types::WITHER),
209 1.0,
210 );
211 }
212
213 true
216 }
217
218 #[must_use]
219 const fn is_shorter_duration_than(&self, other: &Self) -> bool {
220 !self.is_infinite_duration()
221 && (self.duration < other.duration || other.is_infinite_duration())
222 }
223
224 pub fn update(&mut self, take_over: Self) -> bool {
228 let mut changed = false;
229 let take_over_ambient = take_over.ambient;
230 let take_over_visible = take_over.visible;
231 let take_over_show_icon = take_over.show_icon;
232 if take_over.amplifier > self.amplifier {
233 if take_over.is_shorter_duration_than(self) {
234 let previous_hidden_effect = self.hidden_effect.take();
235 let mut hidden = self.clone();
236 hidden.hidden_effect = previous_hidden_effect;
237 self.hidden_effect = Some(Box::new(hidden));
238 }
239
240 self.amplifier = take_over.amplifier;
241 self.duration = take_over.duration;
242 changed = true;
243 } else if self.is_shorter_duration_than(&take_over) {
244 if take_over.amplifier == self.amplifier {
245 self.duration = take_over.duration;
246 changed = true;
247 } else if let Some(hidden_effect) = &mut self.hidden_effect {
248 hidden_effect.update(take_over);
249 } else {
250 self.hidden_effect = Some(Box::new(take_over));
251 }
252 }
253
254 if (!take_over_ambient && self.ambient) || changed {
255 self.ambient = take_over_ambient;
256 changed = true;
257 }
258
259 if take_over_visible != self.visible {
260 self.visible = take_over_visible;
261 changed = true;
262 }
263
264 if take_over_show_icon != self.show_icon {
265 self.show_icon = take_over_show_icon;
266 changed = true;
267 }
268
269 changed
270 }
271
272 fn tick_duration(&mut self) -> MobEffectTickResult {
273 if !self.has_remaining_duration() {
274 return MobEffectTickResult::Expired;
275 }
276
277 self.tick_down_duration();
278 if self.downgrade_to_hidden_effect() {
279 return MobEffectTickResult::Active { downgraded: true };
280 }
281 if self.has_remaining_duration() {
282 MobEffectTickResult::Active { downgraded: false }
283 } else {
284 MobEffectTickResult::Expired
285 }
286 }
287
288 fn tick_down_duration(&mut self) {
289 if let Some(hidden_effect) = &mut self.hidden_effect {
290 hidden_effect.tick_down_duration();
291 }
292
293 if !self.is_infinite_duration() && self.duration != 0 {
294 self.duration -= 1;
295 }
296 }
297
298 fn downgrade_to_hidden_effect(&mut self) -> bool {
299 if self.duration != 0 {
300 return false;
301 }
302
303 let Some(hidden_effect) = self.hidden_effect.take() else {
304 return false;
305 };
306 let MobEffectInstance {
307 duration,
308 amplifier,
309 ambient,
310 visible,
311 show_icon,
312 hidden_effect,
313 ..
314 } = *hidden_effect;
315 self.duration = duration;
316 self.amplifier = amplifier;
317 self.ambient = ambient;
318 self.visible = visible;
319 self.show_icon = show_icon;
320 self.hidden_effect = hidden_effect;
321 true
322 }
323}
324
325const fn clamp_effect_amplifier(amplifier: i32) -> i32 {
326 if amplifier < MIN_EFFECT_AMPLIFIER {
327 MIN_EFFECT_AMPLIFIER
328 } else if amplifier > MAX_EFFECT_AMPLIFIER {
329 MAX_EFFECT_AMPLIFIER
330 } else {
331 amplifier
332 }
333}
334
335enum MobEffectTickResult {
336 Active { downgraded: bool },
337 Expired,
338}
339
340#[derive(Debug, Clone, PartialEq, Eq)]
342pub enum MobEffectSyncChange {
343 Update {
345 effect: MobEffectInstance,
347 blend_for_self: bool,
349 },
350 Remove {
352 effect: MobEffectRef,
354 },
355}
356
357impl MobEffectSyncChange {
358 #[must_use]
360 pub fn packet(&self, entity_id: i32, is_self_recipient: bool) -> MobEffectSyncPacket {
361 match self {
362 Self::Update {
363 effect,
364 blend_for_self,
365 } => MobEffectSyncPacket::Update(CUpdateMobEffect::new(
366 entity_id,
367 effect.effect,
368 effect.amplifier,
369 effect.duration,
370 MobEffectPacketFlags {
371 ambient: effect.ambient,
372 visible: effect.visible,
373 show_icon: effect.show_icon,
374 blend: *blend_for_self && is_self_recipient,
375 },
376 )),
377 Self::Remove { effect } => {
378 MobEffectSyncPacket::Remove(CRemoveMobEffect::new(entity_id, effect))
379 }
380 }
381 }
382}
383
384#[derive(Debug, Clone)]
386pub enum MobEffectSyncPacket {
387 Update(CUpdateMobEffect),
389 Remove(CRemoveMobEffect),
391}
392
393#[derive(Debug, Clone, PartialEq)]
395pub struct MobEffectDisplayState {
396 pub particles: ParticleList,
398 pub ambient: bool,
400 pub invisible: bool,
402 pub glowing: bool,
404}
405
406#[derive(Debug, Clone, Copy, PartialEq)]
411pub struct LivingTravelInput {
412 sideways: f32,
413 vertical: f32,
414 forward: f32,
415}
416
417impl LivingTravelInput {
418 pub const ZERO: Self = Self::new(0.0, 0.0, 0.0);
420
421 #[must_use]
423 pub const fn new(sideways: f32, vertical: f32, forward: f32) -> Self {
424 Self {
425 sideways,
426 vertical,
427 forward,
428 }
429 }
430
431 #[must_use]
433 pub const fn sideways(self) -> f32 {
434 self.sideways
435 }
436
437 #[must_use]
439 pub const fn vertical(self) -> f32 {
440 self.vertical
441 }
442
443 #[must_use]
445 pub const fn forward(self) -> f32 {
446 self.forward
447 }
448
449 #[must_use]
451 pub const fn dampened(self) -> Self {
452 Self {
453 sideways: self.sideways * 0.98,
454 vertical: self.vertical,
455 forward: self.forward * 0.98,
456 }
457 }
458}
459
460#[derive(Debug, Clone, Copy, PartialEq)]
462pub struct LivingRotationState {
463 y_body_rot: f32,
464 y_body_rot_o: f32,
465 y_head_rot: f32,
466 y_head_rot_o: f32,
467}
468
469impl LivingRotationState {
470 #[must_use]
472 pub const fn new() -> Self {
473 Self {
474 y_body_rot: 0.0,
475 y_body_rot_o: 0.0,
476 y_head_rot: 0.0,
477 y_head_rot_o: 0.0,
478 }
479 }
480
481 #[must_use]
483 pub const fn y_body_rot(self) -> f32 {
484 self.y_body_rot
485 }
486
487 #[must_use]
489 pub const fn y_body_rot_o(self) -> f32 {
490 self.y_body_rot_o
491 }
492
493 #[must_use]
495 pub const fn y_head_rot(self) -> f32 {
496 self.y_head_rot
497 }
498
499 #[must_use]
501 pub const fn y_head_rot_o(self) -> f32 {
502 self.y_head_rot_o
503 }
504}
505
506impl Default for LivingRotationState {
507 fn default() -> Self {
508 Self::new()
509 }
510}
511
512#[derive(Debug, Clone, Copy, PartialEq)]
514pub struct LivingSwingState {
515 swinging: bool,
516 swinging_arm: Option<InteractionHand>,
517 swing_time: i32,
518 old_attack_anim: f32,
519 attack_anim: f32,
520}
521
522impl LivingSwingState {
523 #[must_use]
525 pub const fn new() -> Self {
526 Self {
527 swinging: false,
528 swinging_arm: None,
529 swing_time: 0,
530 old_attack_anim: 0.0,
531 attack_anim: 0.0,
532 }
533 }
534
535 #[must_use]
537 pub const fn swinging(self) -> bool {
538 self.swinging
539 }
540
541 #[must_use]
543 pub const fn swinging_arm(self) -> Option<InteractionHand> {
544 self.swinging_arm
545 }
546
547 #[must_use]
549 pub const fn swing_time(self) -> i32 {
550 self.swing_time
551 }
552
553 #[must_use]
555 pub const fn old_attack_anim(self) -> f32 {
556 self.old_attack_anim
557 }
558
559 #[must_use]
561 pub const fn attack_anim(self) -> f32 {
562 self.attack_anim
563 }
564}
565
566impl Default for LivingSwingState {
567 fn default() -> Self {
568 Self::new()
569 }
570}
571
572#[derive(Debug, Clone)]
573struct LivingEntityState {
574 effects_dirty: bool,
575 death_processed: bool,
576 invulnerable_time: i32,
577 last_hurt: f32,
578 last_hurt_by_player: Option<Uuid>,
579 last_hurt_by_player_memory_time: i32,
580 last_hurt_by_mob: Option<WeakEntity>,
581 last_hurt_by_mob_timestamp: i32,
582 last_hurt_mob: Option<WeakEntity>,
583 last_hurt_mob_timestamp: i32,
584 last_damage_source: Option<DamageSource>,
585 last_damage_stamp: i64,
586 absorption_amount: f32,
587 skip_drop_experience: bool,
588 death_time: i32,
589 speed: f32,
590 current_impulse_impact_pos: Option<DVec3>,
591 current_impulse_context_reset_grace_time: i32,
592 fall_flying: bool,
593 fall_flying_ticks: i32,
594 sprinting: bool,
595 sleeping_pos: Option<BlockPos>,
596 last_climbable_pos: Option<BlockPos>,
597 discard_friction: bool,
598 jumping: bool,
599 travel_input: LivingTravelInput,
600 rotation: LivingRotationState,
601 swing: LivingSwingState,
602 no_jump_delay: i32,
603 no_action_time: i32,
604}
605
606impl LivingEntityState {
607 const fn new(speed: f32) -> Self {
608 Self {
609 effects_dirty: false,
610 death_processed: false,
611 invulnerable_time: 0,
612 last_hurt: 0.0,
613 last_hurt_by_player: None,
614 last_hurt_by_player_memory_time: 0,
615 last_hurt_by_mob: None,
616 last_hurt_by_mob_timestamp: 0,
617 last_hurt_mob: None,
618 last_hurt_mob_timestamp: 0,
619 last_damage_source: None,
620 last_damage_stamp: 0,
621 absorption_amount: 0.0,
622 skip_drop_experience: false,
623 death_time: 0,
624 speed,
625 current_impulse_impact_pos: None,
626 current_impulse_context_reset_grace_time: 0,
627 fall_flying: false,
628 fall_flying_ticks: 0,
629 sprinting: false,
630 sleeping_pos: None,
631 last_climbable_pos: None,
632 discard_friction: false,
633 jumping: false,
634 travel_input: LivingTravelInput::ZERO,
635 rotation: LivingRotationState::new(),
636 swing: LivingSwingState::new(),
637 no_jump_delay: 0,
638 no_action_time: 0,
639 }
640 }
641
642 const fn reset_death_state(&mut self) {
643 self.death_processed = false;
644 self.death_time = 0;
645 self.invulnerable_time = 0;
646 self.last_hurt = 0.0;
647 self.absorption_amount = 0.0;
648 self.skip_drop_experience = false;
649 }
650}
651
652pub struct LivingEntityBase {
661 state: SyncMutex<LivingEntityState>,
662 attributes: SyncMutex<AttributeMap>,
663 active_mob_effects: SyncMutex<FxHashMap<MobEffectRef, ActiveMobEffect>>,
664 dirty_mob_effects: SyncMutex<Vec<MobEffectSyncChange>>,
665 equipment: Shared<dyn EntityEquipment>,
666 last_equipment_items: SyncMutex<[ItemStack; EquipmentSlot::ALL.len()]>,
667 pending_equipment_changes: SyncMutex<[Option<ItemStack>; EquipmentSlot::ALL.len()]>,
668 equipment_attribute_modifiers:
669 SyncMutex<[Vec<EquipmentAttributeModifierKey>; EquipmentSlot::ALL.len()]>,
670}
671
672#[derive(Debug)]
673struct EquipmentAttributeModifierKey {
674 attribute: AttributeRef,
675 id: Identifier,
676}
677
678impl LivingEntityBase {
679 #[must_use]
681 pub fn new(entity_type: EntityTypeRef) -> Self {
682 Self::with_attributes(AttributeMap::new_for_entity(entity_type))
683 }
684
685 #[must_use]
687 pub fn with_attributes(attributes: AttributeMap) -> Self {
688 let equipment: Shared<dyn EntityEquipment> = OwnedEntityEquipment::new().into_shared();
689 Self::with_attributes_and_equipment(attributes, equipment)
690 }
691
692 #[must_use]
694 pub fn with_equipment(
695 entity_type: EntityTypeRef,
696 equipment: Shared<dyn EntityEquipment>,
697 ) -> Self {
698 Self::with_attributes_and_equipment(AttributeMap::new_for_entity(entity_type), equipment)
699 }
700
701 fn with_attributes_and_equipment(
702 attributes: AttributeMap,
703 equipment: Shared<dyn EntityEquipment>,
704 ) -> Self {
705 let speed = attributes.required_value(vanilla_attributes::MOVEMENT_SPEED) as f32;
706
707 Self {
708 state: SyncMutex::new(LivingEntityState::new(speed)),
709 attributes: SyncMutex::new(attributes),
710 active_mob_effects: SyncMutex::new(FxHashMap::default()),
711 dirty_mob_effects: SyncMutex::new(Vec::new()),
712 equipment,
713 last_equipment_items: SyncMutex::new(array::from_fn(|_| ItemStack::empty())),
714 pending_equipment_changes: SyncMutex::new(array::from_fn(|_| None)),
715 equipment_attribute_modifiers: SyncMutex::new(array::from_fn(|_| Vec::new())),
716 }
717 }
718
719 #[inline]
721 pub const fn attributes(&self) -> &SyncMutex<AttributeMap> {
722 &self.attributes
723 }
724
725 pub fn initialize_synced_data<T: VanillaLivingEntityData>(&self, entity_data: &mut T) {
730 let max_health = self
731 .attributes
732 .lock()
733 .required_value(vanilla_attributes::MAX_HEALTH) as f32;
734 entity_data.living_entity_mut().health.set(max_health);
735 }
736
737 #[inline]
739 pub const fn equipment(&self) -> &Shared<dyn EntityEquipment> {
740 &self.equipment
741 }
742
743 pub fn collect_equipment_changes(&self) -> Vec<(EquipmentSlot, ItemStack, ItemStack)> {
745 let current_items: [ItemStack; EquipmentSlot::ALL.len()] = {
746 let equipment = self.equipment.lock();
747 array::from_fn(|index| equipment.get_ref(EquipmentSlot::ALL[index]).clone())
748 };
749 let mut last_items = self.last_equipment_items.lock();
750 let mut changes = Vec::new();
751
752 for slot in EquipmentSlot::ALL {
753 let index = slot.index();
754 if ItemStack::matches(&last_items[index], ¤t_items[index]) {
755 continue;
756 }
757 let previous = mem::replace(&mut last_items[index], current_items[index].clone());
758 changes.push((slot, previous, current_items[index].clone()));
759 }
760 changes
761 }
762
763 pub fn queue_equipment_changes(
765 &self,
766 changes: impl IntoIterator<Item = (EquipmentSlot, ItemStack)>,
767 ) {
768 let mut pending = self.pending_equipment_changes.lock();
769 for (slot, item_stack) in changes {
770 pending[slot.index()] = Some(item_stack);
771 }
772 }
773
774 pub fn drain_equipment_changes(&self) -> Vec<(EquipmentSlot, ItemStack)> {
776 let mut pending = self.pending_equipment_changes.lock();
777 EquipmentSlot::ALL
778 .into_iter()
779 .filter_map(|slot| pending[slot.index()].take().map(|item| (slot, item)))
780 .collect()
781 }
782
783 #[must_use]
785 pub fn rotation_state(&self) -> LivingRotationState {
786 self.state.lock().rotation
787 }
788
789 #[must_use]
791 pub fn swing_state(&self) -> LivingSwingState {
792 self.state.lock().swing
793 }
794
795 #[must_use]
797 pub fn y_body_rot(&self) -> f32 {
798 self.state.lock().rotation.y_body_rot
799 }
800
801 pub fn set_y_body_rot(&self, y_body_rot: f32) {
803 self.state.lock().rotation.y_body_rot = y_body_rot;
804 }
805
806 #[must_use]
808 pub fn y_head_rot(&self) -> f32 {
809 self.state.lock().rotation.y_head_rot
810 }
811
812 pub fn set_y_head_rot(&self, y_head_rot: f32) {
814 self.state.lock().rotation.y_head_rot = y_head_rot;
815 }
816
817 pub fn advance_rotation_for_base_tick(&self) {
819 let mut state = self.state.lock();
820 state.rotation.y_head_rot_o = state.rotation.y_head_rot;
821 state.rotation.y_body_rot_o = state.rotation.y_body_rot;
822 }
823
824 pub fn advance_attack_animation_for_base_tick(&self) {
826 let mut state = self.state.lock();
827 state.swing.old_attack_anim = state.swing.attack_anim;
828 }
829
830 pub fn start_swing(&self, hand: InteractionHand, current_swing_duration: i32) -> bool {
832 let mut state = self.state.lock();
833 let swing = &mut state.swing;
834 if swing.swinging && swing.swing_time < current_swing_duration / 2 && swing.swing_time >= 0
835 {
836 return false;
837 }
838
839 swing.swing_time = -1;
840 swing.swinging = true;
841 swing.swinging_arm = Some(hand);
842 true
843 }
844
845 pub fn update_swing_time(&self, current_swing_duration: i32) {
847 let mut state = self.state.lock();
848 let swing = &mut state.swing;
849 if swing.swinging {
850 swing.swing_time += 1;
851 if swing.swing_time >= current_swing_duration {
852 swing.swing_time = 0;
853 swing.swinging = false;
854 }
855 } else {
856 swing.swing_time = 0;
857 }
858
859 swing.attack_anim = swing.swing_time as f32 / current_swing_duration as f32;
860 }
861
862 #[must_use]
864 pub fn absorption_amount(&self) -> f32 {
865 self.state.lock().absorption_amount
866 }
867
868 pub fn set_absorption_amount(&self, amount: f32) {
870 let max_absorption = self
871 .attributes
872 .lock()
873 .required_value(vanilla_attributes::MAX_ABSORPTION) as f32;
874 self.state.lock().absorption_amount = amount.clamp(0.0, max_absorption);
875 }
876
877 pub fn skip_drop_experience(&self) {
879 self.state.lock().skip_drop_experience = true;
880 }
881
882 #[must_use]
884 pub fn was_experience_consumed(&self) -> bool {
885 self.state.lock().skip_drop_experience
886 }
887
888 #[must_use]
890 pub fn no_action_time(&self) -> i32 {
891 self.state.lock().no_action_time
892 }
893
894 pub fn set_no_action_time(&self, no_action_time: i32) {
896 self.state.lock().no_action_time = no_action_time;
897 }
898
899 pub fn increment_no_action_time(&self) {
901 self.state.lock().no_action_time += 1;
902 }
903
904 pub fn refresh_equipment_attribute_modifiers(
906 &self,
907 slot: EquipmentSlot,
908 item_stack: &ItemStack,
909 ) {
910 let slot_index = slot.index();
911 let mut attributes = self.attributes.lock();
912 let mut installed_modifiers = self.equipment_attribute_modifiers.lock();
913
914 for key in installed_modifiers[slot_index].drain(..) {
915 attributes.remove_modifier(key.attribute, &key.id);
916 }
917
918 if item_stack.is_empty() || item_stack.is_broken() {
919 return;
920 }
921
922 let Some(modifiers) = item_stack.get_attribute_modifiers() else {
923 return;
924 };
925
926 for entry in modifiers.for_slot(slot) {
927 for (index, keys) in installed_modifiers.iter_mut().enumerate() {
928 if index == slot_index {
929 continue;
930 }
931 keys.retain(|key| key.attribute.key != entry.attribute.key || key.id != entry.id);
932 }
933
934 attributes.remove_modifier(entry.attribute, &entry.id);
935 if attributes.add_modifier(
936 entry.attribute,
937 AttributeModifier {
938 id: entry.id.clone(),
939 amount: entry.amount,
940 operation: entry.operation,
941 },
942 false,
943 ) {
944 installed_modifiers[slot_index].push(EquipmentAttributeModifierKey {
945 attribute: entry.attribute,
946 id: entry.id.clone(),
947 });
948 }
949 }
950 }
951
952 #[must_use]
954 pub fn has_mob_effect(&self, effect: MobEffectRef) -> bool {
955 self.active_mob_effects.lock().contains_key(&effect)
956 }
957
958 #[must_use]
960 pub fn mob_effect(&self, effect: MobEffectRef) -> Option<ActiveMobEffect> {
961 self.active_mob_effects.lock().get(&effect).cloned()
962 }
963
964 #[must_use]
966 pub fn active_mob_effects(&self) -> Vec<ActiveMobEffect> {
967 self.active_mob_effects.lock().values().cloned().collect()
968 }
969
970 pub fn add_mob_effect(&self, effect: MobEffectInstance) -> bool {
972 let effect_key = effect.effect;
973 let mut existing_effect = None;
974 let mut changed_effect = None;
975 {
976 let mut effects = self.active_mob_effects.lock();
977 if let Some(current) = effects.get_mut(&effect_key) {
978 if current.update(effect) {
979 changed_effect = Some(current.clone());
980 }
981 } else {
982 effects.insert(effect_key, effect.clone());
983 existing_effect = Some(effect);
984 }
985 }
986
987 if let Some(effect) = existing_effect {
988 self.add_effect_attribute_modifiers(&effect);
989 self.mark_effects_dirty();
990 self.queue_mob_effect_sync(MobEffectSyncChange::Update {
991 effect,
992 blend_for_self: true,
993 });
994 return true;
995 }
996
997 if let Some(effect) = changed_effect {
998 self.refresh_effect_attribute_modifiers(&effect);
999 self.mark_effects_dirty();
1000 self.queue_mob_effect_sync(MobEffectSyncChange::Update {
1001 effect,
1002 blend_for_self: false,
1003 });
1004 return true;
1005 }
1006
1007 false
1008 }
1009
1010 pub fn set_mob_effect(&self, effect: MobEffectRef, amplifier: i32) {
1012 self.add_mob_effect(MobEffectInstance::new(effect, amplifier));
1013 }
1014
1015 pub fn set_mob_effect_active(&self, effect: MobEffectRef, active: bool) {
1017 if active {
1018 self.set_mob_effect(effect, 0);
1019 } else {
1020 self.remove_mob_effect(effect);
1021 }
1022 }
1023
1024 pub fn remove_mob_effect(&self, effect: MobEffectRef) -> bool {
1026 let removed = self.active_mob_effects.lock().remove(&effect);
1027 let Some(removed) = removed else {
1028 return false;
1029 };
1030
1031 self.remove_effect_attribute_modifiers(removed.effect);
1032 self.mark_effects_dirty();
1033 self.queue_mob_effect_sync(MobEffectSyncChange::Remove { effect });
1034 true
1035 }
1036
1037 pub(super) fn tick_mob_effect_duration(&self, effect_key: MobEffectRef) {
1039 let (updated, removed) = {
1040 let mut effects = self.active_mob_effects.lock();
1041 let Some(effect) = effects.get_mut(&effect_key) else {
1042 return;
1043 };
1044 match effect.tick_duration() {
1045 MobEffectTickResult::Active { downgraded } => {
1046 let updated =
1047 (downgraded || effect.duration() % 600 == 0).then(|| effect.clone());
1048 (updated, None)
1049 }
1050 MobEffectTickResult::Expired => (None, effects.remove(&effect_key)),
1051 }
1052 };
1053
1054 if let Some(effect) = updated {
1055 self.refresh_effect_attribute_modifiers(&effect);
1056 self.mark_effects_dirty();
1057 self.queue_mob_effect_sync(MobEffectSyncChange::Update {
1058 effect,
1059 blend_for_self: false,
1060 });
1061 }
1062
1063 if let Some(effect) = removed {
1064 self.remove_effect_attribute_modifiers(effect.effect);
1065 self.mark_effects_dirty();
1066 self.queue_mob_effect_sync(MobEffectSyncChange::Remove {
1067 effect: effect.effect,
1068 });
1069 }
1070 }
1071
1072 pub fn drain_dirty_mob_effects(&self) -> Vec<MobEffectSyncChange> {
1074 self.dirty_mob_effects.lock().drain(..).collect()
1075 }
1076
1077 pub fn take_effects_dirty(&self) -> bool {
1079 let mut state = self.state.lock();
1080 let dirty = state.effects_dirty;
1081 state.effects_dirty = false;
1082 dirty
1083 }
1084
1085 pub fn mob_effect_display_state(&self) -> MobEffectDisplayState {
1087 let mut effects = self
1088 .active_mob_effects
1089 .lock()
1090 .values()
1091 .cloned()
1092 .collect::<Vec<_>>();
1093 effects.sort_by_key(|effect| effect.effect.try_id().unwrap_or(usize::MAX));
1094
1095 let particles = effects
1096 .iter()
1097 .filter(|effect| effect.is_visible())
1098 .map(|effect| effect.effect.create_particle_options(effect.ambient))
1099 .collect();
1100
1101 MobEffectDisplayState {
1102 particles: ParticleList { particles },
1103 ambient: !effects.is_empty() && effects.iter().all(MobEffectInstance::is_ambient),
1104 invisible: effects
1105 .iter()
1106 .any(|effect| effect.effect == vanilla_mob_effects::INVISIBILITY),
1107 glowing: effects
1108 .iter()
1109 .any(|effect| effect.effect == vanilla_mob_effects::GLOWING),
1110 }
1111 }
1112
1113 fn add_effect_attribute_modifiers(&self, effect: &MobEffectInstance) {
1114 let mut attributes = self.attributes.lock();
1115 for modifier in effect.effect.attribute_modifiers {
1116 attributes.remove_modifier(modifier.attribute, &modifier.id);
1117 attributes.add_modifier(
1118 modifier.attribute,
1119 AttributeModifier {
1120 id: modifier.id.clone(),
1121 amount: modifier.amount * f64::from(effect.amplifier + 1),
1122 operation: modifier.operation,
1123 },
1124 false,
1125 );
1126 }
1127 }
1128
1129 fn refresh_effect_attribute_modifiers(&self, effect: &MobEffectInstance) {
1130 self.remove_effect_attribute_modifiers(effect.effect);
1131 self.add_effect_attribute_modifiers(effect);
1132 }
1133
1134 fn remove_effect_attribute_modifiers(&self, effect: MobEffectRef) {
1135 let mut attributes = self.attributes.lock();
1136 for modifier in effect.attribute_modifiers {
1137 attributes.remove_modifier(modifier.attribute, &modifier.id);
1138 }
1139 }
1140
1141 fn queue_mob_effect_sync(&self, change: MobEffectSyncChange) {
1142 self.dirty_mob_effects.lock().push(change);
1143 }
1144
1145 pub(crate) fn mark_effects_dirty(&self) {
1147 self.state.lock().effects_dirty = true;
1148 }
1149
1150 #[inline]
1152 pub fn speed(&self) -> f32 {
1153 self.state.lock().speed
1154 }
1155
1156 #[inline]
1158 pub fn set_speed(&self, speed: f32) {
1159 self.state.lock().speed = speed;
1160 }
1161
1162 pub fn refresh_speed_from_attributes(&self) {
1164 if let Some(speed) = self
1165 .attributes
1166 .lock()
1167 .get_value(vanilla_attributes::MOVEMENT_SPEED)
1168 {
1169 self.state.lock().speed = speed as f32;
1170 }
1171 }
1172
1173 pub fn apply_post_impulse_grace_time(&self, ticks: i32) {
1175 let mut state = self.state.lock();
1176 state.current_impulse_context_reset_grace_time =
1177 state.current_impulse_context_reset_grace_time.max(ticks);
1178 }
1179
1180 pub fn set_ignore_fall_damage_from_current_impulse(
1182 &self,
1183 ignore_fall_damage: bool,
1184 new_impulse_impact_pos: DVec3,
1185 ) {
1186 let mut state = self.state.lock();
1187 if ignore_fall_damage {
1188 state.current_impulse_context_reset_grace_time = state
1189 .current_impulse_context_reset_grace_time
1190 .max(POST_IMPULSE_GRACE_TICKS);
1191 state.current_impulse_impact_pos = Some(new_impulse_impact_pos);
1192 } else {
1193 state.current_impulse_context_reset_grace_time = 0;
1194 }
1195 }
1196
1197 #[must_use]
1199 pub fn current_impulse_impact_pos(&self) -> Option<DVec3> {
1200 self.state.lock().current_impulse_impact_pos
1201 }
1202
1203 #[must_use]
1205 pub fn current_impulse_context_reset_grace_time(&self) -> i32 {
1206 self.state.lock().current_impulse_context_reset_grace_time
1207 }
1208
1209 #[must_use]
1211 pub fn is_ignoring_fall_damage_from_current_impulse(&self) -> bool {
1212 self.state.lock().current_impulse_impact_pos.is_some()
1213 }
1214
1215 pub fn try_reset_current_impulse_context(&self) {
1217 let mut state = self.state.lock();
1218 if state.current_impulse_context_reset_grace_time == 0 {
1219 state.current_impulse_impact_pos = None;
1220 }
1221 }
1222
1223 pub fn reset_current_impulse_context(&self) {
1225 let mut state = self.state.lock();
1226 state.current_impulse_context_reset_grace_time = 0;
1227 state.current_impulse_impact_pos = None;
1228 }
1229
1230 #[must_use]
1232 pub fn is_in_post_impulse_grace_time(&self) -> bool {
1233 self.state.lock().current_impulse_context_reset_grace_time > 0
1234 }
1235
1236 pub fn tick_post_impulse_grace_time(&self) {
1238 let mut state = self.state.lock();
1239 if state.current_impulse_context_reset_grace_time > 0 {
1240 state.current_impulse_context_reset_grace_time -= 1;
1241 }
1242 }
1243
1244 #[must_use]
1246 pub fn is_fall_flying(&self) -> bool {
1247 self.state.lock().fall_flying
1248 }
1249
1250 pub fn set_fall_flying(&self, fall_flying: bool) {
1252 self.state.lock().fall_flying = fall_flying;
1253 }
1254
1255 #[must_use]
1257 pub fn fall_flying_ticks(&self) -> i32 {
1258 self.state.lock().fall_flying_ticks
1259 }
1260
1261 pub fn tick_fall_flying_state(&self, fall_flying: bool) {
1263 let mut state = self.state.lock();
1264 if fall_flying {
1265 state.fall_flying_ticks = state.fall_flying_ticks.wrapping_add(1);
1266 } else {
1267 state.fall_flying_ticks = 0;
1268 }
1269 }
1270
1271 #[must_use]
1273 pub fn is_sprinting(&self) -> bool {
1274 self.state.lock().sprinting
1275 }
1276
1277 pub fn set_sprinting(&self, sprinting: bool) {
1279 self.state.lock().sprinting = sprinting;
1280
1281 let mut attributes = self.attributes.lock();
1282 if sprinting {
1283 attributes.add_modifier(
1284 vanilla_attributes::MOVEMENT_SPEED,
1285 AttributeModifier {
1286 id: Identifier::vanilla_static("sprinting"),
1287 amount: SPRINT_SPEED_MODIFIER_AMOUNT,
1288 operation: AttributeModifierOperation::AddMultipliedTotal,
1289 },
1290 false,
1291 );
1292 } else {
1293 attributes.remove_modifier(
1294 vanilla_attributes::MOVEMENT_SPEED,
1295 &Identifier::vanilla_static("sprinting"),
1296 );
1297 }
1298 }
1299
1300 #[must_use]
1302 pub fn sleeping_pos(&self) -> Option<BlockPos> {
1303 self.state.lock().sleeping_pos
1304 }
1305
1306 pub fn set_sleeping_pos(&self, bed_position: BlockPos) {
1308 self.state.lock().sleeping_pos = Some(bed_position);
1309 }
1310
1311 pub fn clear_sleeping_pos(&self) {
1313 self.state.lock().sleeping_pos = None;
1314 }
1315
1316 #[must_use]
1318 pub fn is_sleeping(&self) -> bool {
1319 self.sleeping_pos().is_some()
1320 }
1321
1322 #[must_use]
1324 pub fn last_climbable_pos(&self) -> Option<BlockPos> {
1325 self.state.lock().last_climbable_pos
1326 }
1327
1328 pub fn set_last_climbable_pos(&self, pos: BlockPos) {
1330 self.state.lock().last_climbable_pos = Some(pos);
1331 }
1332
1333 #[must_use]
1335 pub fn should_discard_friction(&self) -> bool {
1336 self.state.lock().discard_friction
1337 }
1338
1339 pub fn set_discard_friction(&self, discard_friction: bool) {
1341 self.state.lock().discard_friction = discard_friction;
1342 }
1343
1344 #[must_use]
1346 pub fn is_jumping(&self) -> bool {
1347 self.state.lock().jumping
1348 }
1349
1350 pub fn set_jumping(&self, jumping: bool) {
1352 self.state.lock().jumping = jumping;
1353 }
1354
1355 #[must_use]
1357 pub fn travel_input(&self) -> LivingTravelInput {
1358 self.state.lock().travel_input
1359 }
1360
1361 pub fn set_travel_input(&self, input: LivingTravelInput) {
1363 self.state.lock().travel_input = input;
1364 }
1365
1366 pub fn dampen_travel_input(&self) {
1368 let mut state = self.state.lock();
1369 state.travel_input = state.travel_input.dampened();
1370 }
1371
1372 #[must_use]
1374 pub fn no_jump_delay(&self) -> i32 {
1375 self.state.lock().no_jump_delay
1376 }
1377
1378 pub fn set_no_jump_delay(&self, ticks: i32) {
1380 self.state.lock().no_jump_delay = ticks;
1381 }
1382
1383 pub fn tick_no_jump_delay(&self) {
1385 let mut state = self.state.lock();
1386 if state.no_jump_delay > 0 {
1387 state.no_jump_delay -= 1;
1388 }
1389 }
1390
1391 #[must_use]
1393 pub fn calculate_fall_damage(
1394 fall_distance: f64,
1395 damage_modifier: f32,
1396 safe_fall_distance: f64,
1397 fall_damage_multiplier: f64,
1398 ) -> i32 {
1399 ((fall_distance + 1.0e-6 - safe_fall_distance)
1400 * f64::from(damage_modifier)
1401 * fall_damage_multiplier)
1402 .floor() as i32
1403 }
1404
1405 pub fn decrement_invulnerable_time(&self) {
1407 let mut state = self.state.lock();
1408 if state.invulnerable_time > 0 {
1409 state.invulnerable_time -= 1;
1410 }
1411 }
1412
1413 pub fn apply_damage_cooldown(
1418 &self,
1419 amount: f32,
1420 bypasses_cooldown: bool,
1421 ) -> Option<(bool, f32)> {
1422 let mut state = self.state.lock();
1423 if state.death_processed {
1424 return None;
1425 }
1426
1427 if state.invulnerable_time > 10 && !bypasses_cooldown {
1428 if amount <= state.last_hurt {
1429 return None;
1430 }
1431 let effective = amount - state.last_hurt;
1432 state.last_hurt = amount;
1433 Some((false, effective))
1434 } else {
1435 state.last_hurt = amount;
1436 state.invulnerable_time = 20;
1437 Some((true, amount))
1438 }
1439 }
1440
1441 pub fn record_last_damage_source(&self, source: &DamageSource, game_time: i64) {
1443 let mut state = self.state.lock();
1444 state.last_damage_source = Some(source.clone());
1445 state.last_damage_stamp = game_time;
1446 }
1447
1448 pub fn last_damage_source(&self, game_time: i64) -> Option<DamageSource> {
1450 let mut state = self.state.lock();
1451 if game_time - state.last_damage_stamp > 40 {
1452 state.last_damage_source = None;
1453 }
1454 state.last_damage_source.clone()
1455 }
1456
1457 pub fn set_last_hurt_by_player(&self, player_uuid: Uuid, time_to_remember: i32) {
1459 let mut state = self.state.lock();
1460 state.last_hurt_by_player = Some(player_uuid);
1461 state.last_hurt_by_player_memory_time = time_to_remember;
1462 }
1463
1464 #[must_use]
1466 pub fn last_hurt_by_player_memory_time(&self) -> i32 {
1467 self.state.lock().last_hurt_by_player_memory_time
1468 }
1469
1470 #[must_use]
1472 pub fn last_hurt_by_player_uuid(&self) -> Option<Uuid> {
1473 self.state.lock().last_hurt_by_player
1474 }
1475
1476 #[must_use]
1478 pub fn last_hurt_by_mob(&self) -> Option<SharedEntity> {
1479 let mut state = self.state.lock();
1480 living_entity_from_weak(&mut state.last_hurt_by_mob)
1481 }
1482
1483 #[must_use]
1485 pub fn last_hurt_by_mob_timestamp(&self) -> i32 {
1486 self.state.lock().last_hurt_by_mob_timestamp
1487 }
1488
1489 pub fn set_last_hurt_by_mob(&self, target: Option<&SharedEntity>, tick_count: i32) {
1491 let mut state = self.state.lock();
1492 state.last_hurt_by_mob = weak_living_entity(target);
1493 state.last_hurt_by_mob_timestamp = tick_count;
1494 }
1495
1496 #[must_use]
1498 pub fn last_hurt_mob(&self) -> Option<SharedEntity> {
1499 let mut state = self.state.lock();
1500 living_entity_from_weak(&mut state.last_hurt_mob)
1501 }
1502
1503 #[must_use]
1505 pub fn last_hurt_mob_timestamp(&self) -> i32 {
1506 self.state.lock().last_hurt_mob_timestamp
1507 }
1508
1509 pub fn set_last_hurt_mob(&self, target: Option<&SharedEntity>, tick_count: i32) {
1511 let mut state = self.state.lock();
1512 state.last_hurt_mob = weak_living_entity(target);
1513 state.last_hurt_mob_timestamp = tick_count;
1514 }
1515
1516 pub fn tick_last_hurt_by_player_memory(&self) {
1518 let mut state = self.state.lock();
1519 if state.last_hurt_by_player_memory_time > 0 {
1520 state.last_hurt_by_player_memory_time -= 1;
1521 } else {
1522 state.last_hurt_by_player = None;
1523 }
1524 }
1525
1526 pub fn tick_living_combat_memory(&self, tick_count: i32) {
1528 if self
1529 .last_hurt_mob()
1530 .is_some_and(|target| living_is_dead(&target))
1531 {
1532 self.set_last_hurt_mob(None, tick_count);
1533 }
1534
1535 let Some(hurt_by) = self.last_hurt_by_mob() else {
1536 return;
1537 };
1538 if living_is_dead(&hurt_by) || tick_count - self.last_hurt_by_mob_timestamp() > 100 {
1539 self.set_last_hurt_by_mob(None, tick_count);
1540 }
1541 }
1542
1543 pub fn mark_death_processed(&self) -> bool {
1547 let mut state = self.state.lock();
1548 if state.death_processed {
1549 return false;
1550 }
1551 state.death_processed = true;
1552 true
1553 }
1554
1555 #[inline]
1557 pub fn increment_death_time(&self) -> i32 {
1558 let mut state = self.state.lock();
1559 state.death_time += 1;
1560 state.death_time
1561 }
1562
1563 #[must_use]
1565 pub fn death_time(&self) -> i32 {
1566 self.state.lock().death_time
1567 }
1568
1569 #[inline]
1571 pub fn reset_death_state(&self) {
1572 self.state.lock().reset_death_state();
1573 }
1574
1575 pub fn reset_for_player_respawn(&self) {
1577 self.set_sprinting(false);
1578
1579 *self.last_equipment_items.lock() = array::from_fn(|_| ItemStack::empty());
1583 *self.pending_equipment_changes.lock() = array::from_fn(|_| None);
1584 {
1585 let mut attributes = self.attributes.lock();
1586 let mut installed_modifiers = self.equipment_attribute_modifiers.lock();
1587 for modifiers in installed_modifiers.iter_mut() {
1588 for key in modifiers.drain(..) {
1589 attributes.remove_modifier(key.attribute, &key.id);
1590 }
1591 }
1592 }
1593
1594 let removed_effects = {
1595 let mut effects = self.active_mob_effects.lock();
1596 let removed_effects = effects.keys().copied().collect::<Vec<_>>();
1597 effects.clear();
1598 removed_effects
1599 };
1600
1601 for effect in removed_effects.iter().copied() {
1602 self.remove_effect_attribute_modifiers(effect);
1603 }
1604
1605 {
1606 let mut dirty_effects = self.dirty_mob_effects.lock();
1607 dirty_effects.clear();
1608 dirty_effects.extend(
1609 removed_effects
1610 .into_iter()
1611 .map(|effect| MobEffectSyncChange::Remove { effect }),
1612 );
1613 }
1614
1615 let speed = self
1616 .attributes
1617 .lock()
1618 .required_value(vanilla_attributes::MOVEMENT_SPEED) as f32;
1619
1620 let mut state = self.state.lock();
1621 *state = LivingEntityState::new(speed);
1622 state.effects_dirty = true;
1623 }
1624}
1625
1626fn weak_living_entity(target: Option<&SharedEntity>) -> Option<WeakEntity> {
1627 let target = target?;
1628 target.is_living_entity().then(|| Arc::downgrade(target))
1629}
1630
1631fn living_entity_from_weak(entity: &mut Option<WeakEntity>) -> Option<SharedEntity> {
1632 let Some(upgraded) = entity.as_ref().and_then(WeakEntity::upgrade) else {
1633 *entity = None;
1634 return None;
1635 };
1636 if !upgraded.is_living_entity() {
1637 *entity = None;
1638 return None;
1639 }
1640 Some(upgraded)
1641}
1642
1643fn living_is_dead(entity: &SharedEntity) -> bool {
1644 entity
1645 .as_living_entity()
1646 .is_none_or(|living| !LivingEntity::is_alive(living))
1647}
1648
1649#[cfg(test)]
1650mod tests;