use super::{Mob, MobEntity}; use crate::entity::NbtFuture; use crate::entity::ai::goal::break_door::BreakDoorGoal; use crate::entity::ai::goal::destroy_egg::DestroyEggGoal; use crate::entity::ai::goal::look_around::RandomLookAroundGoal; use crate::entity::ai::goal::revenge::RevengeGoal; use crate::entity::ai::goal::swim::SwimGoal; use crate::entity::ai::goal::wander_around::WanderAroundGoal; use crate::entity::ai::goal::zombie_attack::ZombieAttackGoal; use crate::entity::{ Entity, ai::goal::{Goal, active_target::ActiveTargetGoal, look_at_entity::LookAtEntityGoal}, }; use pumpkin_data::entity::EntityType; use pumpkin_nbt::compound::NbtCompound; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Weak}; pub mod drowned; pub mod husk; #[allow(clippy::module_inception)] pub mod zombie; pub mod zombie_villager; pub struct ZombieEntityBase { pub mob_entity: MobEntity, pub can_break_doors: AtomicBool, } impl ZombieEntityBase { pub fn new(entity: Entity) -> Arc { Self::with_can_break_doors(entity, false) } pub fn with_can_break_doors(entity: Entity, can_break_doors: bool) -> Arc { let mob_entity = MobEntity::new(entity); let zombie = Self { mob_entity, can_break_doors: AtomicBool::new(can_break_doors), }; let mob_arc = Arc::new(zombie); 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); let mut target_selector = mob_arc .mob_entity .target_selector .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); goal_selector.add_goal(0, Box::new(SwimGoal::default())); if can_break_doors { goal_selector.add_goal(1, Box::new(BreakDoorGoal::default())); } goal_selector.add_goal(2, ZombieAttackGoal::new(1.0, false)); goal_selector.add_goal(4, DestroyEggGoal::new(1.0, 3)); goal_selector.add_goal(7, Box::new(WanderAroundGoal::new(1.0))); goal_selector.add_goal( 8, LookAtEntityGoal::with_default(mob_weak, &EntityType::PLAYER, 8.0), ); goal_selector.add_goal(8, Box::new(RandomLookAroundGoal::default())); target_selector.add_goal(1, Box::new(RevengeGoal::new(true))); target_selector.add_goal( 2, ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::PLAYER, true), ); target_selector.add_goal( 3, ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::VILLAGER, true), ); target_selector.add_goal( 3, ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::IRON_GOLEM, true), ); target_selector.add_goal( 5, ActiveTargetGoal::with_default(&mob_arc.mob_entity, &EntityType::TURTLE, true), ); }; mob_arc } #[must_use] pub fn can_break_doors(&self) -> bool { self.can_break_doors.load(Ordering::Relaxed) } pub async fn set_can_break_doors(&self, can_break_doors: bool, mob: &dyn Mob) { if self .can_break_doors .swap(can_break_doors, Ordering::Relaxed) != can_break_doors { let mut stopped = { let mut goal_selector = self .mob_entity .goals_selector .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); if can_break_doors { goal_selector.add_goal(1, Box::new(BreakDoorGoal::default())); Vec::new() } else { goal_selector.remove_goal_sync::() } }; for goal in &mut stopped { goal.stop(mob).await; } } } } impl Mob for ZombieEntityBase { fn get_mob_entity(&self) -> &MobEntity { &self.mob_entity } fn mob_write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { if self.can_break_doors() { nbt.put_bool("CanBreakDoors", true); } }) } fn mob_read_nbt<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> { Box::pin(async move { if let Some(can_break_doors) = nbt.get_bool("CanBreakDoors") { self.set_can_break_doors(can_break_doors, self).await; } }) } }