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