mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
feat: added some Love 💞
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::{
|
||||
data_component::DataComponent,
|
||||
data_component_impl::DataComponentImpl,
|
||||
item::Item,
|
||||
tag::{RegistryKey, get_tag_ids},
|
||||
};
|
||||
@@ -120,27 +119,15 @@ impl<'a> FindArg<'a> for ItemArgumentConsumer {
|
||||
)
|
||||
{
|
||||
// Match the DataComponent key
|
||||
if let Some(data_comp) = DataComponent::try_from_name(comp_key)
|
||||
{
|
||||
// Handle Profile or other data components
|
||||
match data_comp {
|
||||
DataComponent::Profile => {
|
||||
if let Some(profile_impl) = pumpkin_data::data_component_impl::ProfileImpl::read_data(&nbt_tag) {
|
||||
patch.push((data_comp, Some(profile_impl.to_dyn())));
|
||||
}
|
||||
}
|
||||
DataComponent::CustomData => {
|
||||
if let pumpkin_nbt::tag::NbtTag::Compound(compound) = nbt_tag {
|
||||
patch.push((data_comp, Some(pumpkin_data::data_component_impl::CustomDataImpl { data: compound }.to_dyn())));
|
||||
}
|
||||
}
|
||||
DataComponent::CustomName => {
|
||||
if let pumpkin_nbt::tag::NbtTag::String(text_str) = nbt_tag {
|
||||
patch.push((data_comp, Some(pumpkin_data::data_component_impl::CustomNameImpl { name: pumpkin_util::text::TextComponent::text(String::from(text_str)) }.to_dyn())));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let (Some(data_comp), Some(comp_impl)) = (
|
||||
DataComponent::try_from_name(comp_key),
|
||||
pumpkin_data::data_component_impl::read_data(
|
||||
DataComponent::try_from_name(comp_key)
|
||||
.unwrap_or(DataComponent::CustomData),
|
||||
&nbt_tag,
|
||||
),
|
||||
) {
|
||||
patch.push((data_comp, Some(comp_impl)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
141
pumpkin/src/entity/ageable.rs
Normal file
141
pumpkin/src/entity/ageable.rs
Normal file
@@ -0,0 +1,141 @@
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering::Relaxed};
|
||||
|
||||
use crate::entity::mob::Mob;
|
||||
|
||||
pub const BABY_START_AGE: i32 = -24000;
|
||||
pub const FORCED_AGE_PARTICLE_TICKS: i32 = 40;
|
||||
|
||||
pub struct AgeableData {
|
||||
pub forced_age: AtomicI32,
|
||||
pub forced_age_timer: AtomicI32,
|
||||
pub age_locked: AtomicBool,
|
||||
pub age_lock_particle_timer: AtomicI32,
|
||||
}
|
||||
|
||||
impl Default for AgeableData {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
forced_age: AtomicI32::new(0),
|
||||
forced_age_timer: AtomicI32::new(0),
|
||||
age_locked: AtomicBool::new(false),
|
||||
age_lock_particle_timer: AtomicI32::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AgeableMob: Mob {
|
||||
fn get_ageable_data(&self) -> &AgeableData;
|
||||
|
||||
fn get_baby_start_age(&self) -> i32 {
|
||||
BABY_START_AGE
|
||||
}
|
||||
|
||||
fn is_baby(&self) -> bool {
|
||||
self.get_mob_entity().living_entity.entity.age.load(Relaxed) < 0
|
||||
}
|
||||
|
||||
fn set_baby(&self, baby: bool) {
|
||||
self.set_age(if baby { self.get_baby_start_age() } else { 0 });
|
||||
}
|
||||
|
||||
fn get_age(&self) -> i32 {
|
||||
self.get_mob_entity().living_entity.entity.age.load(Relaxed)
|
||||
}
|
||||
|
||||
fn set_age(&self, new_age: i32) {
|
||||
let mob = self.get_mob_entity();
|
||||
let entity = &mob.living_entity.entity;
|
||||
let old_age = entity.age.swap(new_age, Relaxed);
|
||||
|
||||
if (old_age < 0 && new_age >= 0) || (old_age >= 0 && new_age < 0) {
|
||||
let is_baby = new_age < 0;
|
||||
entity.send_meta_data(
|
||||
&[Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
is_baby,
|
||||
)],
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_age_locked(&self) -> bool {
|
||||
self.get_ageable_data().age_locked.load(Relaxed)
|
||||
}
|
||||
|
||||
fn set_age_locked(&self, locked: bool) {
|
||||
self.get_ageable_data().age_locked.store(locked, Relaxed);
|
||||
}
|
||||
|
||||
fn can_age_up(&self) -> bool {
|
||||
self.is_baby() && !self.is_age_locked()
|
||||
}
|
||||
|
||||
fn age_up(&self, seconds: i32, forced: bool) {
|
||||
let mut age = self.get_age();
|
||||
let old_age = age;
|
||||
age += seconds * 20;
|
||||
if age > 0 {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
let delta = age - old_age;
|
||||
self.set_age(age);
|
||||
|
||||
let data = self.get_ageable_data();
|
||||
if forced {
|
||||
data.forced_age.fetch_add(delta, Relaxed);
|
||||
if data.forced_age_timer.load(Relaxed) == 0 {
|
||||
data.forced_age_timer.store(40, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
if self.get_age() == 0 {
|
||||
self.set_age(data.forced_age.load(Relaxed));
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn get_speed_up_seconds_when_feeding(ticks_until_adult: i32) -> i32 {
|
||||
(ticks_until_adult as f32 / 20.0 * 0.1) as i32
|
||||
}
|
||||
|
||||
fn write_ageable_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
if self.can_be_a_baby() {
|
||||
nbt.put_int("Age", self.get_age());
|
||||
nbt.put_int(
|
||||
"ForcedAge",
|
||||
self.get_ageable_data().forced_age.load(Relaxed),
|
||||
);
|
||||
nbt.put_bool("AgeLocked", self.is_age_locked());
|
||||
}
|
||||
}
|
||||
|
||||
fn read_ageable_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) {
|
||||
if self.can_be_a_baby() {
|
||||
self.set_age(nbt.get_int("Age").unwrap_or(0));
|
||||
self.get_ageable_data()
|
||||
.forced_age
|
||||
.store(nbt.get_int("ForcedAge").unwrap_or(0), Relaxed);
|
||||
self.set_age_locked(nbt.get_bool("AgeLocked").unwrap_or(false));
|
||||
}
|
||||
}
|
||||
|
||||
fn can_be_a_baby(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ageable_ai_step(&self) {
|
||||
if self.can_age_up() {
|
||||
let age = self.get_age() + 1;
|
||||
self.set_age(age);
|
||||
} else if self.get_age() > 0 {
|
||||
let age = self.get_age() - 1;
|
||||
self.set_age(age);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -601,7 +601,19 @@ impl<T: Mob + Send + 'static> EntityBase for T {
|
||||
}
|
||||
|
||||
if mob_entity.love_ticks.load(Relaxed) > 0 {
|
||||
mob_entity.love_ticks.fetch_sub(1, Relaxed);
|
||||
let ticks = mob_entity.love_ticks.fetch_sub(1, Relaxed);
|
||||
if ticks % 10 == 0 {
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
let pos = entity.pos.load();
|
||||
let world = entity.world.load();
|
||||
world.spawn_particle(
|
||||
pos + Vector3::new(0.0, f64::from(entity.height()) + 0.5, 0.0),
|
||||
Vector3::new(0.5, 0.5, 0.5),
|
||||
1.0,
|
||||
1,
|
||||
pumpkin_data::particle::Particle::Heart,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.mob_tick(caller).await;
|
||||
|
||||
@@ -80,6 +80,7 @@ use std::sync::{
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub mod ageable;
|
||||
pub mod ai;
|
||||
pub mod area_effect_cloud;
|
||||
pub mod attributes;
|
||||
|
||||
105
pumpkin/src/entity/passive/animal.rs
Normal file
105
pumpkin/src/entity/passive/animal.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::particle::Particle;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
|
||||
use crate::entity::{EntityBaseFuture, mob::Mob, player::Player};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
pub trait Animal: Mob {
|
||||
fn is_food(&self, item_stack: &ItemStack) -> bool;
|
||||
|
||||
fn play_eating_sound(&self, sound: Sound) {
|
||||
let mob_entity = self.get_mob_entity();
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
let world = entity.world.load();
|
||||
world.play_sound(sound, SoundCategory::Neutral, &entity.pos.load());
|
||||
}
|
||||
|
||||
fn write_animal_nbt(&self, nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
let mob_entity = self.get_mob_entity();
|
||||
let in_love = mob_entity
|
||||
.love_ticks
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
nbt.put_int("InLove", in_love);
|
||||
if let Some(uuid) = mob_entity.breeder.load() {
|
||||
nbt.put_uuid("LoveCause", uuid);
|
||||
}
|
||||
}
|
||||
|
||||
fn read_animal_nbt(&self, nbt: &pumpkin_nbt::compound::NbtCompound) {
|
||||
let mob_entity = self.get_mob_entity();
|
||||
let in_love = nbt.get_int("InLove").unwrap_or(0);
|
||||
let love_cause = nbt.get_uuid("LoveCause");
|
||||
mob_entity.set_love_ticks(in_love, love_cause);
|
||||
}
|
||||
|
||||
fn animal_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
ambient_sound: Sound,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let mob_entity = self.get_mob_entity();
|
||||
if self.is_food(item_stack) {
|
||||
let age = mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.age
|
||||
.load(std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
if age >= 0 && mob_entity.is_breeding_ready() && !mob_entity.is_in_love() {
|
||||
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
||||
|
||||
mob_entity.set_love_ticks(600, Some(player.gameprofile.id));
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
let world = entity.world.load();
|
||||
let pos = entity.pos.load();
|
||||
|
||||
world.send_entity_status(
|
||||
entity,
|
||||
pumpkin_data::entity::EntityStatus::InLoveHearts,
|
||||
);
|
||||
|
||||
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,
|
||||
);
|
||||
world.play_sound(ambient_sound, SoundCategory::Neutral, &entity.pos.load());
|
||||
return true;
|
||||
}
|
||||
|
||||
if age < 0 {
|
||||
item_stack.decrement_unless_creative(player.gamemode.load(), 1);
|
||||
let speedup = (-age / 10).max(1);
|
||||
mob_entity
|
||||
.living_entity
|
||||
.entity
|
||||
.age
|
||||
.fetch_add(speedup, std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
let entity = &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::HappyVillager,
|
||||
);
|
||||
self.play_eating_sound(ambient_sound);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mob_entity.mob_interact(player, item_stack).await
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,22 +5,22 @@ use std::sync::{
|
||||
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::particle::Particle;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_data::{entity::EntityType, item::Item};
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ageable::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,
|
||||
},
|
||||
mob::{Mob, MobEntity},
|
||||
passive::animal::Animal,
|
||||
player::Player,
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
@@ -41,6 +41,7 @@ pub struct ChickenEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub variant: AtomicU8,
|
||||
egg_lay_time: AtomicI32,
|
||||
pub ageable_data: crate::entity::ageable::AgeableData,
|
||||
}
|
||||
|
||||
impl ChickenEntity {
|
||||
@@ -51,6 +52,7 @@ impl ChickenEntity {
|
||||
mob_entity,
|
||||
variant: AtomicU8::new(1), // Default to temperate
|
||||
egg_lay_time: AtomicI32::new(egg_lay_time),
|
||||
ageable_data: crate::entity::ageable::AgeableData::default(),
|
||||
};
|
||||
let mob_arc = Arc::new(chicken);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
@@ -78,10 +80,18 @@ impl ChickenEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::entity::ageable::AgeableMob for ChickenEntity {
|
||||
fn get_ageable_data(&self) -> &crate::entity::ageable::AgeableData {
|
||||
&self.ageable_data
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for ChickenEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
self.write_ageable_nbt(nbt);
|
||||
self.write_animal_nbt(nbt);
|
||||
nbt.put_int("EggLayTime", self.egg_lay_time.load(Ordering::Relaxed));
|
||||
let variant_str = match self.variant.load(Ordering::Relaxed) {
|
||||
0 => "minecraft:cold",
|
||||
@@ -95,6 +105,8 @@ impl NBTStorage for ChickenEntity {
|
||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
self.read_ageable_nbt(nbt);
|
||||
self.read_animal_nbt(nbt);
|
||||
self.egg_lay_time
|
||||
.store(nbt.get_int("EggLayTime").unwrap_or(6000), Ordering::Relaxed);
|
||||
if let Some(variant_str) = nbt.get_string("variant") {
|
||||
@@ -112,6 +124,12 @@ impl NBTStorage for ChickenEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl super::animal::Animal for ChickenEntity {
|
||||
fn is_food(&self, item_stack: &ItemStack) -> bool {
|
||||
TEMPT_ITEMS.iter().any(|i| i.id == item_stack.item.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mob for ChickenEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
@@ -172,32 +190,7 @@ impl Mob for ChickenEntity {
|
||||
player: &'a Arc<Player>,
|
||||
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, Some(player.gameprofile.id));
|
||||
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,
|
||||
);
|
||||
world.play_sound(
|
||||
Sound::EntityChickenAmbient,
|
||||
SoundCategory::Neutral,
|
||||
&entity.pos.load(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
self.mob_entity.mob_interact(player, item_stack).await
|
||||
})
|
||||
use super::animal::Animal;
|
||||
self.animal_interact(player, item_stack, Sound::EntityChickenAmbient)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
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::sound::Sound;
|
||||
use pumpkin_data::{entity::EntityType, item::Item};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
Entity, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ageable::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,
|
||||
},
|
||||
mob::{Mob, MobEntity},
|
||||
passive::animal::Animal,
|
||||
player::Player,
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
@@ -25,12 +25,16 @@ const TEMPT_ITEMS: &[&Item] = &[&Item::WHEAT];
|
||||
/// Wiki: <https://minecraft.wiki/w/Cow>
|
||||
pub struct CowEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub ageable_data: crate::entity::ageable::AgeableData,
|
||||
}
|
||||
|
||||
impl CowEntity {
|
||||
pub fn new(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let cow = Self { mob_entity };
|
||||
let cow = Self {
|
||||
mob_entity,
|
||||
ageable_data: crate::entity::ageable::AgeableData::default(),
|
||||
};
|
||||
let mob_arc = Arc::new(cow);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
@@ -57,13 +61,33 @@ impl CowEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::entity::ageable::AgeableMob for CowEntity {
|
||||
fn get_ageable_data(&self) -> &crate::entity::ageable::AgeableData {
|
||||
&self.ageable_data
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for CowEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
self.mob_entity.living_entity.write_nbt(nbt)
|
||||
Box::pin(async move {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
self.write_ageable_nbt(nbt);
|
||||
self.write_animal_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)
|
||||
Box::pin(async move {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
self.read_ageable_nbt(nbt);
|
||||
self.read_animal_nbt(nbt);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::animal::Animal for CowEntity {
|
||||
fn is_food(&self, item_stack: &ItemStack) -> bool {
|
||||
TEMPT_ITEMS.iter().any(|i| i.id == item_stack.item.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,32 +101,7 @@ impl Mob for CowEntity {
|
||||
player: &'a Arc<Player>,
|
||||
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, Some(player.gameprofile.id));
|
||||
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,
|
||||
);
|
||||
world.play_sound(
|
||||
Sound::EntityCowAmbient,
|
||||
SoundCategory::Neutral,
|
||||
&entity.pos.load(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
self.mob_entity.mob_interact(player, item_stack).await
|
||||
})
|
||||
use super::animal::Animal;
|
||||
self.animal_interact(player, item_stack, Sound::EntityCowAmbient)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod allay;
|
||||
pub mod animal;
|
||||
pub mod armadillo;
|
||||
pub mod axolotl;
|
||||
pub mod bee;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
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::sound::Sound;
|
||||
use pumpkin_data::{entity::EntityType, item::Item};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
Entity, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ageable::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,
|
||||
},
|
||||
mob::{Mob, MobEntity},
|
||||
passive::animal::Animal,
|
||||
player::Player,
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
@@ -30,12 +30,16 @@ const PIG_FOOD: &[&Item] = &[
|
||||
/// Wiki: <https://minecraft.wiki/w/Pig>
|
||||
pub struct PigEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub ageable_data: crate::entity::ageable::AgeableData,
|
||||
}
|
||||
|
||||
impl PigEntity {
|
||||
pub fn new(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let pig = Self { mob_entity };
|
||||
let pig = Self {
|
||||
mob_entity,
|
||||
ageable_data: crate::entity::ageable::AgeableData::default(),
|
||||
};
|
||||
let mob_arc = Arc::new(pig);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
@@ -62,13 +66,33 @@ impl PigEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::entity::ageable::AgeableMob for PigEntity {
|
||||
fn get_ageable_data(&self) -> &crate::entity::ageable::AgeableData {
|
||||
&self.ageable_data
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for PigEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
self.mob_entity.living_entity.write_nbt(nbt)
|
||||
Box::pin(async move {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
self.write_ageable_nbt(nbt);
|
||||
self.write_animal_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)
|
||||
Box::pin(async move {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
self.read_ageable_nbt(nbt);
|
||||
self.read_animal_nbt(nbt);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl super::animal::Animal for PigEntity {
|
||||
fn is_food(&self, item_stack: &ItemStack) -> bool {
|
||||
PIG_FOOD.iter().any(|i| i.id == item_stack.item.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,32 +106,7 @@ impl Mob for PigEntity {
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let is_food = PIG_FOOD.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, Some(player.gameprofile.id));
|
||||
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,
|
||||
);
|
||||
world.play_sound(
|
||||
Sound::EntityPigAmbient,
|
||||
SoundCategory::Neutral,
|
||||
&entity.pos.load(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
self.mob_entity.mob_interact(player, item_stack).await
|
||||
})
|
||||
use super::animal::Animal;
|
||||
self.animal_interact(player, item_stack, Sound::EntityPigAmbient)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
Entity, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ai::goal::{
|
||||
breed::BreedGoal, eat_grass::EatGrassGoal, escape_danger::EscapeDangerGoal,
|
||||
follow_parent::FollowParentGoal, look_around::RandomLookAroundGoal,
|
||||
@@ -22,9 +22,7 @@ use crate::entity::{
|
||||
};
|
||||
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::particle::Particle;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_data::sound::Sound;
|
||||
|
||||
const TEMPT_ITEMS: &[&Item] = &[&Item::WHEAT];
|
||||
|
||||
@@ -129,6 +127,12 @@ impl NBTStorage for SheepEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl super::animal::Animal for SheepEntity {
|
||||
fn is_food(&self, item_stack: &ItemStack) -> bool {
|
||||
TEMPT_ITEMS.iter().any(|i| i.id == item_stack.item.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mob for SheepEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
@@ -145,32 +149,7 @@ impl Mob for SheepEntity {
|
||||
player: &'a Arc<Player>,
|
||||
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, Some(player.gameprofile.id));
|
||||
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,
|
||||
);
|
||||
world.play_sound(
|
||||
Sound::EntitySheepAmbient,
|
||||
SoundCategory::Neutral,
|
||||
&entity.pos.load(),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
self.mob_entity.mob_interact(player, item_stack).await
|
||||
})
|
||||
use super::animal::Animal;
|
||||
self.animal_interact(player, item_stack, Sound::EntitySheepAmbient)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user