use std::sync::{Arc, Weak}; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::{entity::EntityType, item::Item}; use pumpkin_util::math::vector3::Vector3; use crate::entity::{ Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture, ai::goal::{ breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal, look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal, tempt::TemptGoal, wander_around::WanderAroundGoal, }, mob::{Mob, MobEntity}, player::Player, }; use pumpkin_nbt::compound::NbtCompound; const TEMPT_ITEMS: &[&Item] = &[&Item::WHEAT]; /// Represents a Cow, a common passive mob that provides milk, leather, and beef. /// /// Wiki: pub struct CowEntity { pub mob_entity: MobEntity, } impl CowEntity { pub async fn new(entity: Entity) -> Arc { let mob_entity = MobEntity::new(entity); let cow = Self { mob_entity }; let mob_arc = Arc::new(cow); 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().await; goal_selector.add_goal(0, Box::new(SwimGoal::default())); goal_selector.add_goal(1, EscapeDangerGoal::new(2.0)); goal_selector.add_goal(2, BreedGoal::new(1.0)); goal_selector.add_goal(3, Box::new(TemptGoal::new(1.25, TEMPT_ITEMS))); goal_selector.add_goal(4, Box::new(FollowParentGoal::new(1.25))); 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 } } impl NBTStorage for CowEntity { fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { self.mob_entity.living_entity.write_nbt(nbt) } fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { self.mob_entity.living_entity.read_nbt_non_mut(nbt) } } impl Mob for CowEntity { fn get_mob_entity(&self) -> &MobEntity { &self.mob_entity } fn mob_interact<'a>( &'a self, player: &'a Arc, item_stack: &'a mut ItemStack, ) -> EntityBaseFuture<'a, bool> { Box::pin(async move { let is_food = TEMPT_ITEMS.iter().any(|i| i.id == item_stack.item.id); if is_food && self.is_breeding_ready() && !self.is_in_love() { item_stack.decrement_unless_creative(player.gamemode.load(), 1); self.mob_entity.set_love_ticks(600); let entity = &self.mob_entity.living_entity.entity; let world = entity.world.load(); let pos = entity.pos.load(); world .spawn_particle( pos + Vector3::new(0.0, f64::from(entity.height()), 0.0), Vector3::new(0.5, 0.5, 0.5), 1.0, 7, Particle::Heart, ) .await; world .play_sound( Sound::EntityCowAmbient, SoundCategory::Neutral, &entity.pos.load(), ) .await; return true; } false }) } }