use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; use pumpkin_data::entity::EntityType; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::{self, Taggable}; use pumpkin_nbt::compound::NbtCompound; use crate::entity::{ Entity, EntityBase, ageable::{AgeableData, AgeableMob}, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, tempt::TemptGoal, wander_around::WanderAroundGoal, }, item_steerable::{ItemBasedSteering, ItemSteerable}, mob::{Mob, MobEntity}, passive::animal::Animal, player::Player, }; const TEMPT_ITEMS: &[&Item] = &[&Item::WARPED_FUNGUS, &Item::WARPED_FUNGUS_ON_A_STICK]; pub struct StriderEntity { pub mob_entity: MobEntity, pub ageable_data: AgeableData, pub steering: ItemBasedSteering, pub saddled: AtomicBool, pub suffocating: AtomicBool, } impl StriderEntity { pub fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let strider = Self { mob_entity, ageable_data: AgeableData::default(), steering: ItemBasedSteering::default(), saddled: AtomicBool::new(false), suffocating: AtomicBool::new(false), }; let mob_arc = Arc::new(strider); let mob_weak: Weak = { let mob_arc: Arc = mob_arc.clone(); Arc::downgrade(&mob_arc) }; { let mut goal_selector = mob_arc .mob_entity .goals_selector .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); goal_selector.add_goal(0, Box::new(SwimGoal::default())); goal_selector.add_goal(1, EscapeDangerGoal::new(1.65)); goal_selector.add_goal(2, BreedGoal::new(1.0)); goal_selector.add_goal(3, Box::new(TemptGoal::new(1.4, TEMPT_ITEMS))); goal_selector.add_goal(4, Box::new(FollowParentGoal::new(1.1))); goal_selector.add_goal(5, Box::new(WanderAroundGoal::new(1.0))); goal_selector.add_goal( 6, LookAtEntityGoal::with_default(mob_weak, &EntityType::PLAYER, 6.0), ); goal_selector.add_goal(7, Box::new(RandomLookAroundGoal::default())); }; mob_arc } #[must_use] pub fn is_suffocating(&self) -> bool { self.suffocating.load(Ordering::Relaxed) } pub fn set_suffocating(&self, suffocating: bool) { self.suffocating.store(suffocating, Ordering::Relaxed); let entity = self.get_entity(); entity.set_synced_data( pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, suffocating, ); } } impl AgeableMob for StriderEntity { fn get_ageable_data(&self) -> &AgeableData { &self.ageable_data } } impl Animal for StriderEntity { fn is_food(&self, item_stack: &ItemStack) -> bool { item_stack.item.has_tag(&tag::Item::MINECRAFT_STRIDER_FOOD) || item_stack.item == &Item::WARPED_FUNGUS } } impl Mob for StriderEntity { fn as_ageable(&self) -> Option<&dyn AgeableMob> { Some(self) } fn as_animal(&self) -> Option<&dyn Animal> { Some(self) } fn mob_write_nbt(&self, nbt: &mut NbtCompound) { self.write_ageable_nbt(nbt); nbt.put_bool("Saddle", self.is_saddled()); } fn mob_read_nbt(&self, nbt: &NbtCompound) { self.read_ageable_nbt(nbt); if let Some(saddle) = nbt.get_bool("Saddle") { self.set_saddled(saddle); } else if let Some(saddle_byte) = nbt.get_byte("Saddle") { self.set_saddled(saddle_byte == 1); } } fn get_mob_entity(&self) -> &MobEntity { &self.mob_entity } fn get_item_steerable(&self) -> Option<&dyn ItemSteerable> { Some(self) } fn is_saddled(&self) -> bool { self.saddled.load(Ordering::Relaxed) } fn can_be_saddled(&self) -> bool { self.mob_entity.living_entity.entity.is_alive() && !self.is_baby() } fn set_saddled(&self, saddled: bool) { self.saddled.store(saddled, Ordering::Relaxed); } fn mob_tick(&self, _caller: &dyn EntityBase) { self.ageable_ai_step(); } fn mob_init_data_tracker(&self) { let entity = self.get_entity(); let is_baby = entity.age.load(Ordering::Relaxed) < 0; if is_baby { entity.set_synced_data(pumpkin_data::tracked_data::strider::DATA_BABY_ID, true); } entity.set_synced_data( pumpkin_data::tracked_data::strider::DATA_SUFFOCATING, self.is_suffocating(), ); } fn mob_interact(&self, player: &Arc, item_stack: &mut ItemStack) -> bool { let item = item_stack.get_item(); if item == &Item::SADDLE && self.can_be_saddled() && !self.is_saddled() { self.set_saddled(true); item_stack.decrement_unless_creative(player.gamemode.load(), 1); let entity = self.get_entity(); let world = entity.world.load(); let pos = entity.pos.load(); world.play_sound(Sound::EntityStriderSaddle, SoundCategory::Neutral, &pos); return true; } if self.is_saddled() && !self.is_food(item_stack) { let world = player.world(); let ent = &self.mob_entity.living_entity.entity; if let Some(vehicle) = world.get_entity_by_id(ent.entity_id) && let Some(passenger) = world.get_player_by_id(player.entity_id()) { ent.add_passenger(vehicle, passenger as Arc); return true; } } self.animal_interact(player, item_stack, Sound::EntityStriderAmbient) } } impl ItemSteerable for StriderEntity { fn boost(&self) -> bool { self.steering.boost() } fn as_any(&self) -> &dyn std::any::Any { self } }