feat: more entity work
This commit is contained in:
parent
d1cfc0d181
commit
4e614c9eeb
@ -42,7 +42,9 @@ impl ClientPacket for CConfigAddResourcePack<'_> {
|
||||
mut write: impl std::io::Write,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_uuid(self.uuid)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_20_3 {
|
||||
write.write_uuid(self.uuid)?;
|
||||
}
|
||||
write.write_string(self.url)?;
|
||||
write.write_string(self.hash)?;
|
||||
write.write_bool(self.forced)?;
|
||||
|
||||
@ -41,15 +41,19 @@ impl ClientPacket for CAddResourcePack<'_> {
|
||||
mut write: impl std::io::Write,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_uuid(self.uuid)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_20_3 {
|
||||
write.write_uuid(self.uuid)?;
|
||||
}
|
||||
write.write_string(self.url)?;
|
||||
write.write_string(self.hash)?;
|
||||
write.write_bool(self.forced)?;
|
||||
if let Some(prompt) = &self.prompt_message {
|
||||
write.write_bool(true)?;
|
||||
write.write_component(prompt, version)?;
|
||||
} else {
|
||||
write.write_bool(false)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_17 {
|
||||
write.write_bool(self.forced)?;
|
||||
if let Some(prompt) = &self.prompt_message {
|
||||
write.write_bool(true)?;
|
||||
write.write_component(prompt, version)?;
|
||||
} else {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -81,12 +81,19 @@ impl ClientPacket for CCommandSuggestions {
|
||||
mut write: impl std::io::Write,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_var_int(&self.id)?;
|
||||
write.write_var_int(&self.start)?;
|
||||
write.write_var_int(&self.length)?;
|
||||
write.write_var_int(&VarInt(self.matches.len() as i32))?;
|
||||
for match_ in &self.matches {
|
||||
match_.write(&mut write, version)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_13 {
|
||||
write.write_var_int(&self.id)?;
|
||||
write.write_var_int(&self.start)?;
|
||||
write.write_var_int(&self.length)?;
|
||||
write.write_var_int(&VarInt(self.matches.len() as i32))?;
|
||||
for match_ in &self.matches {
|
||||
match_.write(&mut write, version)?;
|
||||
}
|
||||
} else {
|
||||
write.write_var_int(&VarInt(self.matches.len() as i32))?;
|
||||
for match_ in &self.matches {
|
||||
write.write_string(&match_.suggestion)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -24,9 +24,9 @@ impl ClientPacket for CPlaceGhostRecipe<'_> {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_u8(self.window_id)?;
|
||||
write.write_container_id(&crate::VarInt(i32::from(self.window_id)), version)?;
|
||||
write.write_string(self.recipe_id)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -21,10 +21,16 @@ impl ClientPacket for CPlayerRotation {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_f32_be(self.yaw)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_21_9 {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
write.write_f32_be(self.pitch)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_21_9 {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,12 +28,14 @@ impl ClientPacket for CProjectilePower {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_var_int(&self.entity_id)?;
|
||||
write.write_f64_be(self.x_power)?;
|
||||
write.write_f64_be(self.y_power)?;
|
||||
write.write_f64_be(self.z_power)?;
|
||||
if *version < JavaMinecraftVersion::V_1_21 {
|
||||
write.write_f64_be(self.y_power)?;
|
||||
write.write_f64_be(self.z_power)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,10 +26,19 @@ impl ClientPacket for CRemoveMobEffect {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_var_int(&self.entity_id)?;
|
||||
write.write_var_int(&self.effect_id)?;
|
||||
if *version <= JavaMinecraftVersion::V_1_7_6 {
|
||||
write.write_i32_be(self.entity_id.0)?;
|
||||
} else {
|
||||
write.write_var_int(&self.entity_id)?;
|
||||
}
|
||||
|
||||
if *version >= JavaMinecraftVersion::V_1_18_2 {
|
||||
write.write_var_int(&self.effect_id)?;
|
||||
} else {
|
||||
write.write_u8(self.effect_id.0 as u8)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,12 +24,36 @@ impl ClientPacket for CServerData<'_> {
|
||||
mut write: impl std::io::Write,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_component(self.motd, version)?;
|
||||
if let Some(icon) = self.icon_base64 {
|
||||
write.write_bool(true)?;
|
||||
write.write_string(icon)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_19_4 {
|
||||
write.write_component(self.motd, version)?;
|
||||
if let Some(icon) = self.icon_base64 {
|
||||
write.write_bool(true)?;
|
||||
let raw_b64 = icon.strip_prefix("data:image/png;base64,").unwrap_or(icon);
|
||||
if let Ok(bytes) = pumpkin_util::jwt::decode_b64_standard(raw_b64) {
|
||||
write.write_slice(&bytes)?;
|
||||
} else {
|
||||
write.write_slice(&[])?;
|
||||
}
|
||||
} else {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
} else {
|
||||
write.write_bool(false)?;
|
||||
write.write_bool(true)?;
|
||||
write.write_component(self.motd, version)?;
|
||||
if let Some(icon) = self.icon_base64 {
|
||||
write.write_bool(true)?;
|
||||
write.write_string(icon)?;
|
||||
} else {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
if *version < JavaMinecraftVersion::V_1_19_3 {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
if *version >= JavaMinecraftVersion::V_1_19_1
|
||||
&& *version < JavaMinecraftVersion::V_1_20_5
|
||||
{
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -20,9 +20,13 @@ impl ClientPacket for CSetSelectedSlot {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_i8(self.slot)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_21_4 {
|
||||
write.write_var_int(&crate::VarInt(i32::from(self.slot)))?;
|
||||
} else {
|
||||
write.write_i8(self.slot)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,13 +38,40 @@ impl ClientPacket for CUpdateMobEffect {
|
||||
fn write_packet_data(
|
||||
&self,
|
||||
mut write: impl std::io::Write,
|
||||
_version: &JavaMinecraftVersion,
|
||||
version: &JavaMinecraftVersion,
|
||||
) -> Result<(), crate::ser::WritingError> {
|
||||
write.write_var_int(&self.entity_id)?;
|
||||
write.write_var_int(&self.effect_id)?;
|
||||
write.write_var_int(&self.amplifier)?;
|
||||
write.write_var_int(&self.duration)?;
|
||||
write.write_i8(self.flags)?;
|
||||
if *version <= JavaMinecraftVersion::V_1_7_6 {
|
||||
write.write_i32_be(self.entity_id.0)?;
|
||||
} else {
|
||||
write.write_var_int(&self.entity_id)?;
|
||||
}
|
||||
|
||||
if *version >= JavaMinecraftVersion::V_1_18_2 {
|
||||
write.write_var_int(&self.effect_id)?;
|
||||
} else {
|
||||
write.write_u8(self.effect_id.0 as u8)?;
|
||||
}
|
||||
|
||||
if *version >= JavaMinecraftVersion::V_1_20_5 {
|
||||
write.write_var_int(&self.amplifier)?;
|
||||
} else {
|
||||
write.write_i8(self.amplifier.0 as i8)?;
|
||||
}
|
||||
|
||||
if *version <= JavaMinecraftVersion::V_1_7_6 {
|
||||
write.write_i16_be(self.duration.0 as i16)?;
|
||||
} else {
|
||||
write.write_var_int(&self.duration)?;
|
||||
}
|
||||
|
||||
if *version > JavaMinecraftVersion::V_1_7_6 {
|
||||
write.write_i8(self.flags)?;
|
||||
}
|
||||
|
||||
if *version >= JavaMinecraftVersion::V_1_19 && *version < JavaMinecraftVersion::V_1_20_5 {
|
||||
write.write_bool(false)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,27 +45,38 @@ impl ClientPacket for CUpdateObjectives {
|
||||
write.write_string(&self.objective_name)?;
|
||||
write.write_u8(self.mode)?;
|
||||
if self.mode == 0 || self.mode == 2 {
|
||||
write.write_component(&self.display_name, version)?;
|
||||
write.write_var_int(&self.render_type)?;
|
||||
write.write_option(&self.number_format, |p, v| {
|
||||
match v {
|
||||
NumberFormat::Blank => p.write_var_int(&VarInt(0)),
|
||||
NumberFormat::Styled(_style) => {
|
||||
p.write_var_int(&VarInt(1))?;
|
||||
let comp = pumpkin_nbt::compound::NbtCompound::new();
|
||||
// Write style properties if any
|
||||
let bytes = pumpkin_nbt::Nbt::from(comp).write_unnamed();
|
||||
p.write_all(&bytes).map_err(WritingError::IoError)
|
||||
}
|
||||
NumberFormat::Fixed(text_component) => {
|
||||
p.write_var_int(&VarInt(2))?;
|
||||
p.write_component(text_component, version)
|
||||
}
|
||||
if *version < JavaMinecraftVersion::V_1_13 {
|
||||
write.write_string(&self.display_name.clone().to_pretty_console())?;
|
||||
let render_str = if self.render_type.0 == 1 {
|
||||
"hearts"
|
||||
} else {
|
||||
"integer"
|
||||
};
|
||||
write.write_string(render_str)?;
|
||||
} else {
|
||||
write.write_component(&self.display_name, version)?;
|
||||
write.write_var_int(&self.render_type)?;
|
||||
if *version >= JavaMinecraftVersion::V_1_20_3 {
|
||||
write.write_option(&self.number_format, |p, v| {
|
||||
match v {
|
||||
NumberFormat::Blank => p.write_var_int(&VarInt(0)),
|
||||
NumberFormat::Styled(_style) => {
|
||||
p.write_var_int(&VarInt(1))?;
|
||||
let comp = pumpkin_nbt::compound::NbtCompound::new();
|
||||
// Write style properties if any
|
||||
let bytes = pumpkin_nbt::Nbt::from(comp).write_unnamed();
|
||||
p.write_all(&bytes).map_err(WritingError::IoError)
|
||||
}
|
||||
NumberFormat::Fixed(text_component) => {
|
||||
p.write_var_int(&VarInt(2))?;
|
||||
p.write_component(text_component, version)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -66,9 +66,52 @@ impl ClientPacket for CUpdateScore {
|
||||
version: &pumpkin_util::version::JavaMinecraftVersion,
|
||||
) -> Result<(), WritingError> {
|
||||
write.write_string(&self.entity_name)?;
|
||||
write.write_string(&self.objective_name)?;
|
||||
write.write_var_int(&self.value)?;
|
||||
write.write_option(&self.display_name, |w, t| w.write_component(t, version))?;
|
||||
write.write_option(&self.number_format, |w, n| n.write(w))
|
||||
if *version >= pumpkin_util::version::JavaMinecraftVersion::V_1_20_3 {
|
||||
write.write_string(&self.objective_name)?;
|
||||
write.write_var_int(&self.value)?;
|
||||
write.write_option(&self.display_name, |w, t| w.write_component(t, version))?;
|
||||
write.write_option(&self.number_format, |w, n| n.write(w))
|
||||
} else if *version <= pumpkin_util::version::JavaMinecraftVersion::V_1_7_6 {
|
||||
write.write_u8(0)?;
|
||||
write.write_string(&self.objective_name)?;
|
||||
write.write_i32_be(self.value.0)
|
||||
} else {
|
||||
write.write_u8(0)?;
|
||||
write.write_string(&self.objective_name)?;
|
||||
write.write_var_int(&self.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pumpkin_util::version::JavaMinecraftVersion;
|
||||
|
||||
#[test]
|
||||
fn update_score_serialization() {
|
||||
let packet = CUpdateScore::new("Alex".into(), "kills".into(), VarInt(42), None, None);
|
||||
|
||||
// Modern 1.20.3+
|
||||
let mut buf_modern = Vec::new();
|
||||
packet
|
||||
.write_packet_data(&mut buf_modern, &JavaMinecraftVersion::V_1_20_3)
|
||||
.unwrap();
|
||||
|
||||
// 1.8 - 1.20.2
|
||||
let mut buf_legacy = Vec::new();
|
||||
packet
|
||||
.write_packet_data(&mut buf_legacy, &JavaMinecraftVersion::V_1_8)
|
||||
.unwrap();
|
||||
|
||||
// 1.7.6
|
||||
let mut buf_v1_7 = Vec::new();
|
||||
packet
|
||||
.write_packet_data(&mut buf_v1_7, &JavaMinecraftVersion::V_1_7_6)
|
||||
.unwrap();
|
||||
|
||||
assert!(!buf_modern.is_empty());
|
||||
assert!(!buf_legacy.is_empty());
|
||||
assert!(!buf_v1_7.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use pumpkin_macros::java_packet;
|
||||
|
||||
use crate::{
|
||||
ServerPacket,
|
||||
ser::{NetworkReadExt, ReadingError},
|
||||
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
|
||||
};
|
||||
use pumpkin_util::version::JavaMinecraftVersion;
|
||||
|
||||
@ -34,11 +34,17 @@ pub struct SConfigResourcePack {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SConfigResourcePack {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
uuid: bytebuf.get_uuid()?,
|
||||
result: bytebuf.get_var_int()?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
let uuid = if *version >= JavaMinecraftVersion::V_1_20_3 {
|
||||
bytebuf.get_uuid()?
|
||||
} else {
|
||||
uuid::Uuid::nil()
|
||||
};
|
||||
if *version < JavaMinecraftVersion::V_1_10 {
|
||||
let _hash = bytebuf.get_str_bounded_borrowed(40)?;
|
||||
}
|
||||
let result = bytebuf.get_var_int()?;
|
||||
Ok(Self { uuid, result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,11 +16,28 @@ pub struct SCommandSuggestion<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SCommandSuggestion<'a> {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
id: bytebuf.get_var_int()?,
|
||||
command: bytebuf.get_str_borrowed()?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
if *version >= JavaMinecraftVersion::V_1_13 {
|
||||
Ok(Self {
|
||||
id: bytebuf.get_var_int()?,
|
||||
command: bytebuf.get_str_borrowed()?,
|
||||
})
|
||||
} else {
|
||||
let command = bytebuf.get_str_borrowed()?;
|
||||
if *version >= JavaMinecraftVersion::V_1_9 {
|
||||
let _assume_command = bytebuf.get_bool()?;
|
||||
}
|
||||
if *version >= JavaMinecraftVersion::V_1_8 {
|
||||
let has_pos = bytebuf.get_bool()?;
|
||||
if has_pos {
|
||||
let _ = bytebuf.get_block_pos(version)?;
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
id: VarInt(0),
|
||||
command,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,21 +16,49 @@ pub struct SEditBook<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SEditBook<'a> {
|
||||
fn read(read: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
let slot = read.get_var_int()?;
|
||||
let count = read.get_var_int()?.0 as usize;
|
||||
let count = count.min(100);
|
||||
let mut pages = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
pages.push(read.get_str_bounded_borrowed(1024)?);
|
||||
}
|
||||
let has_title = read.get_bool()?;
|
||||
let title = if has_title {
|
||||
Some(read.get_str_bounded_borrowed(128)?)
|
||||
fn read(read: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
if *version >= JavaMinecraftVersion::V_1_17_1 {
|
||||
let slot = read.get_var_int()?;
|
||||
let count = read.get_var_int()?.0 as usize;
|
||||
let max_pages = if *version >= JavaMinecraftVersion::V_1_21_2 {
|
||||
100
|
||||
} else {
|
||||
200
|
||||
};
|
||||
let count = count.min(max_pages);
|
||||
let char_limit = if *version >= JavaMinecraftVersion::V_1_21_2 {
|
||||
1024
|
||||
} else {
|
||||
8192
|
||||
};
|
||||
let mut pages = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
pages.push(read.get_str_bounded_borrowed(char_limit)?);
|
||||
}
|
||||
let has_title = read.get_bool()?;
|
||||
let title_limit = if *version >= JavaMinecraftVersion::V_1_21_2 {
|
||||
32
|
||||
} else {
|
||||
128
|
||||
};
|
||||
let title = if has_title {
|
||||
Some(read.get_str_bounded_borrowed(title_limit)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self { slot, pages, title })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Self { slot, pages, title })
|
||||
let _item = crate::codec::item_stack_seralizer::ItemStackSerializer::read_with_version(
|
||||
read, version,
|
||||
)?;
|
||||
let _signing = read.get_bool()?;
|
||||
let slot = read.get_var_int()?;
|
||||
Ok(Self {
|
||||
slot,
|
||||
pages: Vec::new(),
|
||||
title: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ServerPacket,
|
||||
ser::{NetworkReadExt, ReadingError},
|
||||
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
|
||||
};
|
||||
use pumpkin_data::packet::serverbound::play::PLACE_RECIPE;
|
||||
use pumpkin_macros::java_packet;
|
||||
@ -16,12 +16,35 @@ pub struct SPlaceRecipe {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SPlaceRecipe {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
container_id: bytebuf.get_i8()?,
|
||||
recipe_display_id: bytebuf.get_var_int()?,
|
||||
use_max_items: bytebuf.get_bool()?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
if *version >= JavaMinecraftVersion::V_1_21_2 {
|
||||
let container_id = bytebuf.get_container_id(version)?.0 as i8;
|
||||
let recipe_display_id = bytebuf.get_var_int()?;
|
||||
let use_max_items = bytebuf.get_bool()?;
|
||||
Ok(Self {
|
||||
container_id,
|
||||
recipe_display_id,
|
||||
use_max_items,
|
||||
})
|
||||
} else if *version >= JavaMinecraftVersion::V_1_13 {
|
||||
let container_id = bytebuf.get_i8()?;
|
||||
let _recipe_key = bytebuf.get_str_borrowed()?;
|
||||
let use_max_items = bytebuf.get_bool()?;
|
||||
Ok(Self {
|
||||
container_id,
|
||||
recipe_display_id: VarInt(0),
|
||||
use_max_items,
|
||||
})
|
||||
} else {
|
||||
let container_id = bytebuf.get_i8()?;
|
||||
let recipe_display_id = bytebuf.get_var_int()?;
|
||||
let use_max_items = bytebuf.get_bool()?;
|
||||
Ok(Self {
|
||||
container_id,
|
||||
recipe_display_id,
|
||||
use_max_items,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ServerPacket,
|
||||
ser::{NetworkReadExt, ReadingError},
|
||||
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
|
||||
};
|
||||
use pumpkin_data::packet::serverbound::play::RECIPE_BOOK_SEEN_RECIPE;
|
||||
use pumpkin_macros::java_packet;
|
||||
@ -14,10 +14,17 @@ pub struct SRecipeBookSeenRecipe {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SRecipeBookSeenRecipe {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
recipe_display_id: bytebuf.get_var_int()?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
if *version >= JavaMinecraftVersion::V_1_21_2 {
|
||||
Ok(Self {
|
||||
recipe_display_id: bytebuf.get_var_int()?,
|
||||
})
|
||||
} else {
|
||||
let _recipe = bytebuf.get_str_borrowed()?;
|
||||
Ok(Self {
|
||||
recipe_display_id: VarInt(0),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ use pumpkin_macros::java_packet;
|
||||
use crate::{
|
||||
ServerPacket,
|
||||
codec::var_int::VarInt,
|
||||
ser::{NetworkReadExt, ReadingError},
|
||||
ser::{NetworkReadExt, NetworkReadSliceExt, ReadingError},
|
||||
};
|
||||
use pumpkin_util::version::JavaMinecraftVersion;
|
||||
|
||||
@ -45,11 +45,17 @@ impl SPlayResourcePack {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SPlayResourcePack {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
uuid: bytebuf.get_uuid()?,
|
||||
result: bytebuf.get_var_int()?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
let uuid = if *version >= JavaMinecraftVersion::V_1_20_3 {
|
||||
bytebuf.get_uuid()?
|
||||
} else {
|
||||
uuid::Uuid::nil()
|
||||
};
|
||||
if *version < JavaMinecraftVersion::V_1_10 {
|
||||
let _hash = bytebuf.get_str_bounded_borrowed(40)?;
|
||||
}
|
||||
let result = bytebuf.get_var_int()?;
|
||||
Ok(Self { uuid, result })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,11 +15,22 @@ pub struct SSetBeacon {
|
||||
}
|
||||
|
||||
impl<'a> ServerPacket<'a> for SSetBeacon {
|
||||
fn read(bytebuf: &mut &'a [u8], _version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
Ok(Self {
|
||||
primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
|
||||
secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
|
||||
})
|
||||
fn read(bytebuf: &mut &'a [u8], version: &JavaMinecraftVersion) -> Result<Self, ReadingError> {
|
||||
if *version >= JavaMinecraftVersion::V_1_19 {
|
||||
Ok(Self {
|
||||
primary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
|
||||
secondary_effect: bytebuf.get_option(NetworkReadExt::get_var_int)?,
|
||||
})
|
||||
} else {
|
||||
let p = bytebuf.get_var_int()?;
|
||||
let s = bytebuf.get_var_int()?;
|
||||
let primary_effect = if p.0 == -1 { None } else { Some(p) };
|
||||
let secondary_effect = if s.0 == -1 { None } else { Some(s) };
|
||||
Ok(Self {
|
||||
primary_effect,
|
||||
secondary_effect,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -474,6 +474,18 @@ impl<T: Math + Copy> Mul<T> for Vector3<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + Copy> Div<T> for Vector3<T> {
|
||||
type Output = Self;
|
||||
|
||||
fn div(self, scalar: T) -> Self {
|
||||
Self {
|
||||
x: self.x / scalar,
|
||||
y: self.y / scalar,
|
||||
z: self.z / scalar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Math + Copy> Add for Vector3<T> {
|
||||
type Output = Self;
|
||||
|
||||
|
||||
@ -2,12 +2,12 @@ use crate::block::blocks::falling::FallingBlock;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
PlacedArgs,
|
||||
PathComputationType, PlacedArgs,
|
||||
};
|
||||
|
||||
use pumpkin_data::block_properties::WallTorchLikeProperties;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_data::{Block, BlockStateId};
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId};
|
||||
use pumpkin_inventory::anvil::AnvilScreenHandler;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
@ -79,6 +79,10 @@ impl BlockBehaviour for AnvilBlock {
|
||||
) -> BlockStateId {
|
||||
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct AnvilScreenFactory;
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::entities::bed::BedBlockEntity;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::BedPart;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::GameMode;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
@ -18,7 +17,7 @@ use crate::block::bounce_entity_after_fall;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, NormalUseArgs, OnPlaceArgs, OnStateReplacedArgs,
|
||||
PlacedArgs, PlayerPlacedArgs,
|
||||
PathComputationType, PlacedArgs, PlayerPlacedArgs,
|
||||
};
|
||||
use crate::entity::{Entity, EntityBase, player::Player};
|
||||
use crate::world::World;
|
||||
@ -188,6 +187,10 @@ impl BlockBehaviour for BedBlock {
|
||||
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
|
||||
Self::use_bed(args.world, args.player, args.block, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl BedBlock {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::block::{GetComparatorOutputArgs, PlacedArgs};
|
||||
use crate::block::{GetComparatorOutputArgs, PathComputationType, PlacedArgs};
|
||||
use crate::block::{
|
||||
registry::BlockActionResult,
|
||||
{BlockBehaviour, NormalUseArgs},
|
||||
};
|
||||
|
||||
use crate::block::entities::brewing_stand::BrewingStandBlockEntity;
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
@ -85,4 +86,8 @@ impl BlockBehaviour for BrewingStandBlock {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetComparatorOutputArgs, GetStateForNeighborUpdateArgs,
|
||||
NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, UseWithItemArgs,
|
||||
NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, UseWithItemArgs,
|
||||
blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
|
||||
},
|
||||
entity::player::Player,
|
||||
@ -11,7 +11,7 @@ use crate::{
|
||||
};
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::{
|
||||
Block, BlockStateId,
|
||||
Block, BlockState, BlockStateId,
|
||||
block_properties::CakeLikeProperties,
|
||||
sound::{Sound, SoundCategory},
|
||||
};
|
||||
@ -169,6 +169,10 @@ impl BlockBehaviour for CakeBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockStateId, Enchantment, block_properties::CampfireLikeProperties,
|
||||
damage::DamageType, data_component_impl::EquipmentSlot, effect::StatusEffect, fluid::Fluid,
|
||||
Block, BlockDirection, BlockState, BlockStateId, Enchantment,
|
||||
block_properties::CampfireLikeProperties, damage::DamageType,
|
||||
data_component_impl::EquipmentSlot, effect::StatusEffect, fluid::Fluid,
|
||||
};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -9,7 +10,7 @@ use crate::block::entities::campfire::CampfireBlockEntity;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, BlockIsReplacing, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
|
||||
OnPlaceArgs, PlacedArgs,
|
||||
OnPlaceArgs, PathComputationType, PlacedArgs,
|
||||
},
|
||||
entity::EntityBase,
|
||||
};
|
||||
@ -95,6 +96,10 @@ impl BlockBehaviour for CampfireBlock {
|
||||
props.to_state_id(args.block)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
// TODO: onProjectileHit
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::{Block, BlockStateId, item::Item, item_stack::ItemStack};
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId, item::Item, item_stack::ItemStack};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::{GameMode, math::position::BlockPos};
|
||||
use pumpkin_world::{
|
||||
@ -11,7 +11,7 @@ use pumpkin_world::{
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnScheduledTickArgs,
|
||||
UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult,
|
||||
PathComputationType, UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult,
|
||||
},
|
||||
entity::player::Player,
|
||||
world::World,
|
||||
@ -121,6 +121,10 @@ impl BlockBehaviour for CandleCakeBlock {
|
||||
}
|
||||
args.state_id
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, BlockMetadata, GetComparatorOutputArgs, UseWithItemArgs};
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockId;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockMetadata, GetComparatorOutputArgs, PathComputationType, UseWithItemArgs,
|
||||
};
|
||||
use pumpkin_data::block_properties::WaterCauldronLikeProperties;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::{Block, BlockId, BlockState};
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
pub struct CauldronBlock;
|
||||
@ -216,4 +217,8 @@ impl BlockBehaviour for CauldronBlock {
|
||||
_ => Some(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType};
|
||||
use pumpkin_data::block_properties::Axis;
|
||||
use pumpkin_data::{BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
|
||||
#[pumpkin_block("minecraft:iron_chain")]
|
||||
@ -20,4 +19,8 @@ impl BlockBehaviour for ChainBlock {
|
||||
|
||||
props.to_state_id(args.block)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,17 +22,15 @@ use pumpkin_world::world::BlockFlags;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::block::{
|
||||
BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs, GetRedstonePowerArgs,
|
||||
NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs, PlayerPlacedArgs,
|
||||
RandomTickArgs,
|
||||
BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
|
||||
GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType,
|
||||
PlacedArgs, PlayerPlacedArgs, RandomTickArgs, registry::BlockActionResult,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use crate::world::World;
|
||||
use crate::world::loot::fill_chest_inventory;
|
||||
use crate::{
|
||||
block::{BlockBehaviour, registry::BlockActionResult},
|
||||
entity::player::Player,
|
||||
};
|
||||
use pumpkin_data::BlockState;
|
||||
|
||||
struct ChestScreenFactory(Arc<dyn Inventory>);
|
||||
|
||||
@ -297,6 +295,10 @@ impl BlockBehaviour for ChestBlock {
|
||||
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
|
||||
get_chest_comparator_output(&args)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Copper chests have the same behavior as wooden chests but also oxidize over time.
|
||||
@ -389,6 +391,10 @@ impl BlockBehaviour for CopperChestBlock {
|
||||
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
|
||||
get_chest_comparator_output(&args)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Trapped chests have the same behavior as wooden chests but also emit redstone power based on viewer count.
|
||||
@ -455,6 +461,10 @@ impl BlockBehaviour for TrappedChestBlock {
|
||||
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
|
||||
get_chest_comparator_output(&args)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_chest_props(
|
||||
|
||||
@ -3,13 +3,13 @@ use std::sync::Arc;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs,
|
||||
UseWithItemArgs, registry::BlockActionResult,
|
||||
PathComputationType, UseWithItemArgs, registry::BlockActionResult,
|
||||
},
|
||||
entity::{Entity, item::ItemEntity},
|
||||
world::World,
|
||||
};
|
||||
use pumpkin_data::{
|
||||
Block, BlockStateId, block_properties::ComposterLikeProperties,
|
||||
Block, BlockState, BlockStateId, block_properties::ComposterLikeProperties,
|
||||
composter_increase_chance::get_composter_increase_chance_from_item_id, entity::EntityType,
|
||||
item::Item, item_stack::ItemStack, world::WorldEvent,
|
||||
};
|
||||
@ -93,6 +93,10 @@ impl BlockBehaviour for ComposterBlock {
|
||||
Some(props.level)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ComposterBlock {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::block::entities::conduit::ConduitBlockEntity;
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs};
|
||||
use pumpkin_data::BlockStateId;
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs};
|
||||
use pumpkin_data::{BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -22,4 +22,8 @@ impl BlockBehaviour for ConduitBlock {
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::DecoratedPotLikeProperties;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::{BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
|
||||
use crate::block::entities::decorated_pot::DecoratedPotBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs,
|
||||
UseWithItemArgs,
|
||||
BlockBehaviour, BrokenArgs, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs,
|
||||
PathComputationType, PlacedArgs, UseWithItemArgs,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:decorated_pot")]
|
||||
@ -109,4 +109,8 @@ impl BlockBehaviour for DecoratedPotBlock {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::CanPlaceAtArgs;
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use crate::block::OnScheduledTickArgs;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnScheduledTickArgs, PathComputationType,
|
||||
};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -47,6 +44,10 @@ impl BlockBehaviour for DirtPathBlock {
|
||||
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
||||
can_place_at(args.block_accessor, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@ -7,7 +7,7 @@ use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockState, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::BlockAccessor;
|
||||
@ -22,6 +22,7 @@ use crate::block::NormalUseArgs;
|
||||
use crate::block::OnNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use crate::block::OnStateReplacedArgs;
|
||||
use crate::block::PathComputationType;
|
||||
use crate::block::PlacedArgs;
|
||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
@ -391,6 +392,15 @@ impl BlockBehaviour for DoorBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
match computation_type {
|
||||
PathComputationType::Land | PathComputationType::Air => {
|
||||
DoorProperties::from_state_id(state.id).open
|
||||
}
|
||||
PathComputationType::Water => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_support(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
use crate::block::blocks::falling::FallingBlock;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, BrokenArgs, NormalUseArgs, PlacedArgs};
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, NormalUseArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -56,7 +59,11 @@ impl BlockBehaviour for DragonEggBlock {
|
||||
Self::teleport(args.world, args.position);
|
||||
}
|
||||
|
||||
fn on_scheduled_tick(&self, args: crate::block::OnScheduledTickArgs<'_>) {
|
||||
fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
||||
FallingBlock::on_scheduled_tick(&FallingBlock, args);
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,13 +3,13 @@ use std::sync::Arc;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, BrokenArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
PlacedArgs,
|
||||
PathComputationType, PlacedArgs,
|
||||
},
|
||||
entity::player::Player,
|
||||
world::World,
|
||||
};
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockStateId,
|
||||
Block, BlockDirection, BlockState, BlockStateId,
|
||||
block_properties::{PointedDripstoneLikeProperties, SpeleothemThickness, VerticalDirection},
|
||||
};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
@ -114,6 +114,10 @@ impl BlockBehaviour for DripstoneBlock {
|
||||
}
|
||||
args.state_id
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn update_stalagmite(world: &Arc<World>, stalagmite_len: u8, tip_pos: &BlockPos) {
|
||||
let block_above = world.get_block(&tip_pos.up());
|
||||
|
||||
@ -3,8 +3,8 @@ use std::sync::Mutex;
|
||||
|
||||
use crate::block::entities::enchanting_table::EnchantingTableBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, PlacedArgs};
|
||||
use pumpkin_data::{Block, BlockStateId, translation};
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType, PlacedArgs};
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId, translation};
|
||||
use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
@ -79,6 +79,10 @@ impl BlockBehaviour for EnchantingTableBlock {
|
||||
);
|
||||
BlockActionResult::Success
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl EnchantingTableBlock {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::{BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
|
||||
use crate::{
|
||||
block::{BlockBehaviour, OnPlaceArgs},
|
||||
block::{BlockBehaviour, OnPlaceArgs, PathComputationType},
|
||||
entity::EntityBase,
|
||||
};
|
||||
|
||||
@ -18,4 +18,8 @@ impl BlockBehaviour for EndPortalFrameBlock {
|
||||
|
||||
end_portal_frame_props.to_state_id(args.block)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType};
|
||||
use pumpkin_data::block_properties::EndRodLikeProperties;
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
|
||||
#[pumpkin_block("minecraft:end_rod")]
|
||||
@ -26,4 +25,8 @@ impl BlockBehaviour for EndRodBlock {
|
||||
|
||||
props.to_state_id(args.block)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,13 +3,12 @@ use std::sync::Mutex;
|
||||
|
||||
use crate::block::entities::ender_chest::EnderChestBlockEntity;
|
||||
use crate::block::{
|
||||
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PlacedArgs,
|
||||
registry::BlockActionResult,
|
||||
BlockBehaviour, NormalUseArgs, OnPlaceArgs, OnSyncedBlockEventArgs, PathComputationType,
|
||||
PlacedArgs, registry::BlockActionResult,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::LadderLikeProperties;
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_data::{BlockState, BlockStateId, translation};
|
||||
use pumpkin_inventory::{
|
||||
generic_container_screen_handler::create_generic_9x3,
|
||||
player::ender_chest_inventory::EnderChestInventory,
|
||||
@ -112,6 +111,10 @@ impl BlockBehaviour for EnderChestBlock {
|
||||
let block_entity = EnderChestBlockEntity::new(*args.position);
|
||||
args.world.add_block_entity(Arc::new(block_entity));
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_chest_blocked(world: &World, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::CanPlaceAtArgs;
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use crate::block::OnScheduledTickArgs;
|
||||
use crate::block::RandomTickArgs;
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnScheduledTickArgs, PathComputationType, RandomTickArgs,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::FarmlandLikeProperties;
|
||||
use pumpkin_data::tag;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
@ -93,6 +89,10 @@ impl BlockBehaviour for FarmlandBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@ -3,16 +3,16 @@ use std::sync::Arc;
|
||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs,
|
||||
OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use crate::world::World;
|
||||
use pumpkin_data::Block;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::tag;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
@ -134,6 +134,15 @@ impl BlockBehaviour for FenceGateBlock {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
match computation_type {
|
||||
PathComputationType::Land | PathComputationType::Air => {
|
||||
FenceGateProperties::from_state_id(state.id).open
|
||||
}
|
||||
PathComputationType::Water => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_in_wall(args: &GetStateForNeighborUpdateArgs<'_>) -> FenceGateProperties {
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::HorizontalFacingExt;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
type FenceGateProperties = pumpkin_data::block_properties::OakFenceGateLikeProperties;
|
||||
type FenceProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::world::World;
|
||||
|
||||
#[pumpkin_block_from_tag("minecraft:fences")]
|
||||
pub struct FenceBlock;
|
||||
|
||||
@ -34,6 +29,10 @@ impl BlockBehaviour for FenceBlock {
|
||||
let fence_props = FenceProperties::from_state_id(args.state_id);
|
||||
compute_fence_state(fence_props, args.world, args.block, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_fence_state(
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, RandomTickArgs, UseWithItemArgs};
|
||||
use crate::block::{BlockBehaviour, PathComputationType, RandomTickArgs, UseWithItemArgs};
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::flower_pot_transformations::get_potted_item;
|
||||
use pumpkin_data::{Block, BlockId};
|
||||
use pumpkin_data::{Block, BlockId, BlockState};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
@ -61,4 +61,8 @@ impl BlockBehaviour for FlowerPotBlock {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::HorizontalFacingExt;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
type GlassPaneProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::world::World;
|
||||
|
||||
#[pumpkin_block_from_tag("c:glass_panes")]
|
||||
pub struct GlassPaneBlock;
|
||||
|
||||
@ -32,6 +28,10 @@ impl BlockBehaviour for GlassPaneBlock {
|
||||
let pane_props = GlassPaneProperties::from_state_id(args.state_id);
|
||||
compute_pane_state(pane_props, args.world, args.block, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_pane_state(
|
||||
|
||||
@ -17,10 +17,12 @@ use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_world::inventory::SimpleInventory;
|
||||
use pumpkin_world::world::BlockAccessor;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::CanPlaceAtArgs;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs};
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs,
|
||||
PathComputationType,
|
||||
};
|
||||
use pumpkin_data::BlockState;
|
||||
|
||||
use super::abstract_wall_mounting::WallMountedBlock;
|
||||
|
||||
@ -63,6 +65,10 @@ impl BlockBehaviour for GrindstoneBlock {
|
||||
) -> BlockStateId {
|
||||
WallMountedBlock::get_state_for_neighbor_update(self, args)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl WallMountedBlock for GrindstoneBlock {
|
||||
|
||||
@ -2,7 +2,9 @@ use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||
use crate::block::{GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs};
|
||||
use crate::block::{
|
||||
GetComparatorOutputArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, PlacedArgs,
|
||||
};
|
||||
use crate::block::{
|
||||
registry::BlockActionResult,
|
||||
{BlockBehaviour, NormalUseArgs},
|
||||
@ -10,9 +12,8 @@ use crate::block::{
|
||||
use crate::world::World;
|
||||
|
||||
use crate::block::entities::hopper::HopperBlockEntity;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::FacingHopper;
|
||||
use pumpkin_data::{Block, BlockDirection, translation};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, translation};
|
||||
use pumpkin_inventory::generic_container_screen_handler::create_hopper;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
@ -111,6 +112,10 @@ impl BlockBehaviour for HopperBlock {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_powered_state(world: &Arc<World>, pos: &BlockPos, state_id: BlockStateId, block: &Block) {
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::HorizontalFacingExt;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt, tag};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
type IronBarsProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::world::World;
|
||||
|
||||
#[pumpkin_block("minecraft:iron_bars")]
|
||||
pub struct IronBarsBlock;
|
||||
|
||||
@ -32,6 +28,10 @@ impl BlockBehaviour for IronBarsBlock {
|
||||
let bars_props = IronBarsProperties::from_state_id(args.state_id);
|
||||
compute_bars_state(bars_props, args.world, args.block, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_bars_state(
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnScheduledTickArgs, PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{BlockDirection, tag};
|
||||
use pumpkin_data::{BlockDirection, BlockState, BlockStateId, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -48,6 +48,10 @@ impl BlockBehaviour for LanternBlock {
|
||||
.break_block(args.position, None, BlockFlags::empty());
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &World, position: &BlockPos) -> bool {
|
||||
|
||||
@ -6,7 +6,7 @@ use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
|
||||
GetRedstonePowerArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs,
|
||||
PlacedArgs, UseWithItemArgs,
|
||||
PathComputationType, PlacedArgs, UseWithItemArgs,
|
||||
};
|
||||
use crate::entity::Entity;
|
||||
use crate::entity::item::ItemEntity;
|
||||
@ -16,7 +16,7 @@ use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::world::WorldEvent;
|
||||
use pumpkin_data::{Block, BlockDirection, BlockStateId, tag, translation};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag, translation};
|
||||
use pumpkin_inventory::lectern_screen_handler::{LecternController, LecternScreenHandler};
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
@ -296,4 +296,8 @@ impl BlockBehaviour for LecternBlock {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ use rustc_hash::FxHashMap;
|
||||
use crate::{
|
||||
block::{
|
||||
BlockBehaviour, BlockMetadata, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnSyncedBlockEventArgs, PlacedArgs,
|
||||
OnSyncedBlockEventArgs, PathComputationType, PlacedArgs,
|
||||
blocks::{piston::piston_head::PistonHeadProperties, redstone::is_emitting_redstone_power},
|
||||
},
|
||||
world::World,
|
||||
@ -121,6 +121,10 @@ impl BlockBehaviour for PistonBlock {
|
||||
let block = Block::from_id(block_id);
|
||||
Self::handle_synced_block_event(block, args.world, args.position, args.r#type, args.data)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PistonBlock {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
use pumpkin_data::{Block, FacingExt};
|
||||
use pumpkin_data::{Block, BlockState, FacingExt};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::BrokenArgs;
|
||||
use crate::block::{BlockBehaviour, BrokenArgs, PathComputationType};
|
||||
|
||||
use super::piston::PistonProps;
|
||||
|
||||
@ -29,4 +28,8 @@ impl BlockBehaviour for PistonExtensionBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
use pumpkin_data::block_properties::Facing;
|
||||
use pumpkin_data::{Block, FacingExt};
|
||||
use pumpkin_data::{Block, BlockState, FacingExt};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::blocks::piston::piston::try_move;
|
||||
use crate::block::{BrokenArgs, OnNeighborUpdateArgs};
|
||||
use crate::block::{BlockBehaviour, BrokenArgs, OnNeighborUpdateArgs, PathComputationType};
|
||||
|
||||
use super::piston::PistonProps;
|
||||
|
||||
@ -54,4 +53,8 @@ impl BlockBehaviour for PistonHeadBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::{BambooLeaves, BambooLikeProperties};
|
||||
use pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::tag::{self};
|
||||
use pumpkin_data::{Block, BlockDirection};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
||||
use rand::RngExt;
|
||||
|
||||
use crate::block::{BlockBehaviour, CanPlaceAtArgs, blocks::plant::PlantBlockBase};
|
||||
use crate::block::{
|
||||
GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, RandomTickArgs,
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnScheduledTickArgs, PathComputationType, RandomTickArgs, blocks::plant::PlantBlockBase,
|
||||
};
|
||||
use crate::world::World;
|
||||
|
||||
@ -111,6 +110,10 @@ impl BlockBehaviour for BambooBlock {
|
||||
update_leaves_and_grow(args.world, args.position);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn update_leaves_and_grow(world: &Arc<World>, position: &BlockPos) {
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::CactusLikeProperties;
|
||||
use pumpkin_data::damage::DamageType;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockDirection, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -11,7 +10,7 @@ use rand::RngExt;
|
||||
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnEntityCollisionArgs,
|
||||
OnScheduledTickArgs, RandomTickArgs,
|
||||
OnScheduledTickArgs, PathComputationType, RandomTickArgs,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:cactus")]
|
||||
@ -94,6 +93,10 @@ impl BlockBehaviour for CactusBlock {
|
||||
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
||||
can_place_at(args.block_accessor, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(world: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockStateId,
|
||||
Block, BlockDirection, BlockState, BlockStateId,
|
||||
block_properties::BrownMushroomBlockLikeProperties,
|
||||
tag::{self, Taggable},
|
||||
};
|
||||
@ -11,7 +11,8 @@ use pumpkin_world::{
|
||||
};
|
||||
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnScheduledTickArgs, PathComputationType,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:chorus_plant")]
|
||||
@ -65,6 +66,10 @@ impl BlockBehaviour for ChorusPlantBlock {
|
||||
.break_block(args.position, None, BlockFlags::empty());
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@ -9,7 +9,7 @@ use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
||||
|
||||
use crate::block::{
|
||||
BlockBehaviour, BonemealArgs, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
RandomTickArgs,
|
||||
PathComputationType, RandomTickArgs,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
|
||||
@ -137,4 +137,8 @@ impl BlockBehaviour for CocoaBlock {
|
||||
props.facing = mirror.mirror_horizontal(props.facing);
|
||||
BlockState::from_id(props.to_state_id(block))
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,14 +3,13 @@ use crate::block::blocks::plant::PlantBlockBase;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, CanPlaceAtArgs, CanUpdateAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs,
|
||||
UseWithItemArgs,
|
||||
PathComputationType, UseWithItemArgs,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::entity::EntityPose;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockDirection, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
@ -130,6 +129,10 @@ impl BlockBehaviour for SeaPickleBlock {
|
||||
args.state_id,
|
||||
)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PlantBlockBase for SeaPickleBlock {}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
@ -9,7 +10,7 @@ use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::{BlockBehaviour, OnEntityCollisionArgs, OnLandedUponArgs};
|
||||
use crate::block::{BlockBehaviour, OnEntityCollisionArgs, OnLandedUponArgs, PathComputationType};
|
||||
use crate::entity::EntityBase;
|
||||
|
||||
#[pumpkin_block("minecraft:powder_snow")]
|
||||
@ -117,4 +118,8 @@ impl BlockBehaviour for PowderSnowBlock {
|
||||
|
||||
entity.extinguish();
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,16 +5,15 @@ use crate::block::entities::bell::BellBlockEntity;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs,
|
||||
OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs,
|
||||
OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, PlacedArgs,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::block_properties::{AttachFace, BellAttachment, BellLikeProperties};
|
||||
use pumpkin_data::sound::Sound;
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockDirection};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_data::{HorizontalFacingExt, tag};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
@ -215,4 +214,8 @@ impl BlockBehaviour for BellBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,11 @@ use std::sync::Arc;
|
||||
|
||||
use crate::block::{
|
||||
BlockBehaviour, EmitsRedstonePowerArgs, GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::LightningRodLikeProperties;
|
||||
use pumpkin_data::{BlockStateId, FacingExt};
|
||||
use pumpkin_data::{BlockState, BlockStateId, FacingExt};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
@ -75,4 +76,8 @@ impl BlockBehaviour for LightningRodBlock {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,14 +4,14 @@ use crate::block::entities::calibrated_sculk_sensor::CalibratedSculkSensorBlockE
|
||||
use crate::block::entities::sculk_sensor::SculkSensorBlockEntity;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockMetadata, EmitsRedstonePowerArgs, GetComparatorOutputArgs,
|
||||
GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs,
|
||||
GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::{
|
||||
CalibratedSculkSensorLikeProperties, HorizontalFacing, SculkSensorLikeProperties,
|
||||
SculkSensorPhase,
|
||||
};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockId, BlockStateId};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockId, BlockState, BlockStateId};
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
@ -224,4 +224,8 @@ impl BlockBehaviour for SculkSensorBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,12 @@ use pumpkin_data::block_properties::RespawnAnchorLikeProperties;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_data::{BlockState, translation};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, UseWithItemArgs};
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType, UseWithItemArgs};
|
||||
use crate::entity::EntityBase;
|
||||
|
||||
#[pumpkin_block("minecraft:respawn_anchor")]
|
||||
@ -91,4 +91,8 @@ impl BlockBehaviour for RespawnAnchorBlock {
|
||||
|
||||
BlockActionResult::SuccessServer
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::AcaciaShelfLikeProperties;
|
||||
use pumpkin_data::{BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
|
||||
use crate::block::entities::shelf::ShelfBlockEntity;
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PlacedArgs};
|
||||
use crate::block::{BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs};
|
||||
use crate::entity::EntityBase;
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -26,4 +26,8 @@ impl BlockBehaviour for ShelfBlock {
|
||||
args.world.add_block_entity(Arc::new(entity));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
computation_type == PathComputationType::Water && state.is_waterlogged()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||
use crate::block::{BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs};
|
||||
use crate::block::entities::skull::SkullBlockEntity;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockMetadata, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
PlacedArgs,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use pumpkin_data::BlockId;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::SkeletonSkullLikeProperties;
|
||||
use pumpkin_data::{BlockId, BlockState, BlockStateId};
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
type SkeletonSkullLikeProperties = pumpkin_data::block_properties::SkeletonSkullLikeProperties;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct SkullBlock;
|
||||
|
||||
@ -23,10 +26,6 @@ impl BlockMetadata for SkullBlock {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::block::PlacedArgs;
|
||||
use crate::block::entities::skull::SkullBlockEntity;
|
||||
use std::sync::Arc;
|
||||
|
||||
impl BlockBehaviour for SkullBlock {
|
||||
fn placed(&self, args: PlacedArgs<'_>) {
|
||||
{
|
||||
@ -57,4 +56,8 @@ impl BlockBehaviour for SkullBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::SlabType;
|
||||
use pumpkin_data::{BlockDirection, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::BlockIsReplacing;
|
||||
use crate::block::CanUpdateAtArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockIsReplacing, CanUpdateAtArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
|
||||
type SlabProperties = pumpkin_data::block_properties::ResinBrickSlabLikeProperties;
|
||||
|
||||
@ -49,4 +47,11 @@ impl BlockBehaviour for SlabBlock {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
match computation_type {
|
||||
PathComputationType::Water => state.is_waterlogged(),
|
||||
PathComputationType::Land | PathComputationType::Air => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,12 +2,14 @@ use pumpkin_data::block_properties::SnifferEggLikeProperties;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::{BlockId, BlockStateId};
|
||||
use pumpkin_data::{BlockId, BlockState, BlockStateId};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::tick::TickPriority;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::{BlockBehaviour, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs};
|
||||
use crate::block::{
|
||||
BlockBehaviour, BrokenArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, PlacedArgs,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:sniffer_egg")]
|
||||
pub struct SnifferEggBlock;
|
||||
@ -94,4 +96,8 @@ impl BlockBehaviour for SnifferEggBlock {
|
||||
.drop_stack(args.position, ItemStack::new(1, &Item::SNIFFER_EGG));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, BlockStateId, block_properties::SnowLikeProperties, item::Item, tag};
|
||||
use pumpkin_data::{
|
||||
Block, BlockState, BlockStateId, block_properties::SnowLikeProperties, item::Item, tag,
|
||||
};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::{
|
||||
@ -9,7 +11,7 @@ use pumpkin_world::{
|
||||
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs,
|
||||
RandomTickArgs, UseWithItemArgs, registry::BlockActionResult,
|
||||
PathComputationType, RandomTickArgs, UseWithItemArgs, registry::BlockActionResult,
|
||||
};
|
||||
|
||||
#[pumpkin_block("minecraft:snow")]
|
||||
@ -90,6 +92,11 @@ impl BlockBehaviour for LayeredSnowBlock {
|
||||
}
|
||||
args.state_id
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
computation_type == PathComputationType::Land
|
||||
&& SnowLikeProperties::from_state_id(state.id).layers < 5
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place_at(block_accessor: &dyn BlockAccessor, position: &BlockPos) -> bool {
|
||||
|
||||
@ -8,9 +8,7 @@ use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::block::OnNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use crate::block::{BlockBehaviour, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType};
|
||||
use crate::world::World;
|
||||
|
||||
type StairsProperties = pumpkin_data::block_properties::OakStairsLikeProperties;
|
||||
@ -117,6 +115,10 @@ impl BlockBehaviour for StairBlock {
|
||||
|
||||
BlockState::from_id(state_id)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_stair_shape(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs};
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, PathComputationType};
|
||||
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_data::{BlockState, translation};
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
|
||||
@ -28,6 +28,10 @@ impl BlockBehaviour for StonecutterBlock {
|
||||
|
||||
BlockActionResult::Success
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct StonecutterScreenFactory;
|
||||
|
||||
@ -1,15 +1,16 @@
|
||||
use crate::block::blocks::redstone::block_receives_redstone_power;
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs};
|
||||
use crate::block::{
|
||||
BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use crate::world::World;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use pumpkin_data::block_properties::Half;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockState, BlockStateId, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
@ -135,4 +136,12 @@ impl BlockBehaviour for TrapDoorBlock {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
let props = TrapDoorProperties::from_state_id(state.id);
|
||||
match computation_type {
|
||||
PathComputationType::Land | PathComputationType::Air => props.open,
|
||||
PathComputationType::Water => props.waterlogged,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
use crate::block::GetStateForNeighborUpdateArgs;
|
||||
use crate::block::OnPlaceArgs;
|
||||
use pumpkin_data::BlockDirection;
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_data::BlockStateId;
|
||||
use crate::block::{
|
||||
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
|
||||
};
|
||||
use crate::world::World;
|
||||
use pumpkin_data::block_properties::EastWall;
|
||||
use pumpkin_data::block_properties::HorizontalFacing;
|
||||
use pumpkin_data::block_properties::NorthWall;
|
||||
use pumpkin_data::block_properties::SouthWall;
|
||||
use pumpkin_data::block_properties::WestWall;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::{Block, tag};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
|
||||
use pumpkin_macros::pumpkin_block_from_tag;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
|
||||
use crate::block::BlockBehaviour;
|
||||
use crate::world::World;
|
||||
type FenceGateProperties = pumpkin_data::block_properties::OakFenceGateLikeProperties;
|
||||
type FenceLikeProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
|
||||
type WallProperties = pumpkin_data::block_properties::ResinBrickWallLikeProperties;
|
||||
@ -37,6 +34,10 @@ impl BlockBehaviour for WallBlock {
|
||||
let wall_props = WallProperties::from_state_id(args.state_id);
|
||||
compute_wall_state(wall_props, args.world, args.block, args.position)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_wall_state(
|
||||
|
||||
@ -20,7 +20,7 @@ use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockMetadata, BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs,
|
||||
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
|
||||
OnStateReplacedArgs, PlacedArgs, RandomTickArgs,
|
||||
OnStateReplacedArgs, PathComputationType, PlacedArgs, RandomTickArgs,
|
||||
};
|
||||
use crate::world::World;
|
||||
|
||||
@ -710,6 +710,10 @@ impl BlockBehaviour for WeatheringCopperStairBlock {
|
||||
fn random_tick(&self, args: RandomTickArgs<'_>) {
|
||||
change_over_time(args.world, args.position, args.block);
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
StairBlock.is_pathfindable(state, computation_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Weathering copper trapdoor blocks.
|
||||
@ -768,6 +772,10 @@ impl BlockBehaviour for WeatheringCopperTrapDoorBlock {
|
||||
fn random_tick(&self, args: RandomTickArgs<'_>) {
|
||||
change_over_time(args.world, args.position, args.block);
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
TrapDoorBlock.is_pathfindable(state, computation_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Weathering copper slab blocks.
|
||||
@ -822,6 +830,10 @@ impl BlockBehaviour for WeatheringCopperSlabBlock {
|
||||
fn random_tick(&self, args: RandomTickArgs<'_>) {
|
||||
change_over_time(args.world, args.position, args.block);
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
SlabBlock.is_pathfindable(state, computation_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Weathering copper door blocks.
|
||||
@ -907,6 +919,10 @@ impl BlockBehaviour for WeatheringCopperDoorBlock {
|
||||
change_over_time(args.world, args.position, args.block);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
DoorBlock.is_pathfindable(state, computation_type)
|
||||
}
|
||||
}
|
||||
|
||||
/// Weathering copper grate blocks.
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
use pumpkin_data::{Block, BlockDirection, BlockStateId, entity::EntityType, world::WorldEvent};
|
||||
use pumpkin_data::{
|
||||
Block, BlockDirection, BlockState, BlockStateId, entity::EntityType, world::WorldEvent,
|
||||
};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_world::world::BlockFlags;
|
||||
|
||||
use crate::{
|
||||
block::{BlockBehaviour, OnPlaceArgs, PlacedArgs, blocks::skull_block::SkullBlock},
|
||||
block::{
|
||||
BlockBehaviour, OnPlaceArgs, PathComputationType, PlacedArgs,
|
||||
blocks::skull_block::SkullBlock,
|
||||
},
|
||||
entity::{Entity, boss::wither::WitherEntity},
|
||||
};
|
||||
|
||||
@ -92,4 +97,8 @@ impl BlockBehaviour for WitherSkeletonSkullBlock {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use pumpkin_data::fluid::Fluid;
|
||||
use pumpkin_data::tag::{self, Taggable};
|
||||
use pumpkin_data::{Block, BlockId, BlockState};
|
||||
|
||||
use pumpkin_data::BlockStateId;
|
||||
@ -28,6 +30,13 @@ use pumpkin_util::math::boundingbox::BoundingBox;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_world::world::{BlockAccessor, BlockFlags};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum PathComputationType {
|
||||
Land,
|
||||
Water,
|
||||
Air,
|
||||
}
|
||||
|
||||
pub trait BlockMetadata {
|
||||
fn ids() -> Box<[BlockId]>;
|
||||
}
|
||||
@ -185,6 +194,17 @@ pub trait BlockBehaviour: Send + Sync {
|
||||
) -> &'static BlockState {
|
||||
block.rotate(state_id, rotation)
|
||||
}
|
||||
|
||||
fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool {
|
||||
match computation_type {
|
||||
PathComputationType::Water => {
|
||||
state.is_waterlogged()
|
||||
|| Fluid::from_state_id(state.id)
|
||||
.is_some_and(|f| f.has_tag(&tag::Fluid::MINECRAFT_WATER))
|
||||
}
|
||||
PathComputationType::Land | PathComputationType::Air => !state.is_full_cube(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
||||
@ -160,7 +160,8 @@ use crate::block::fluid::water::FlowingWater;
|
||||
use crate::block::{
|
||||
BlockBehaviour, BlockHitResult, BlockMetadata, BonemealArgs, FluidMetadata,
|
||||
GetInsideCollisionShapeArgs, OnEntityCollisionArgs, OnEntityStepArgs, OnLandedUponArgs,
|
||||
OnProjectileHitArgs, UpdateEntityMovementAfterFallOnArgs, stop_vertical_movement_after_fall,
|
||||
OnProjectileHitArgs, PathComputationType, UpdateEntityMovementAfterFallOnArgs,
|
||||
stop_vertical_movement_after_fall,
|
||||
};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
@ -172,6 +173,7 @@ use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
use pumpkin_data::fluid::Fluid;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::tag::{self, Taggable};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockId, BlockState};
|
||||
use pumpkin_protocol::java::server::play::SUseItemOn;
|
||||
use pumpkin_util::math::boundingbox::BoundingBox;
|
||||
@ -1385,4 +1387,24 @@ impl BlockRegistry {
|
||||
|pumpkin_block| pumpkin_block.rotate(block, state_id, rotation),
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_pathfindable(
|
||||
&self,
|
||||
block: &Block,
|
||||
state: &BlockState,
|
||||
computation_type: PathComputationType,
|
||||
) -> bool {
|
||||
self.get_pumpkin_block(block.id).map_or_else(
|
||||
|| match computation_type {
|
||||
PathComputationType::Water => {
|
||||
state.is_waterlogged()
|
||||
|| Fluid::from_state_id(state.id)
|
||||
.is_some_and(|f| f.has_tag(&tag::Fluid::MINECRAFT_WATER))
|
||||
}
|
||||
PathComputationType::Land | PathComputationType::Air => !state.is_full_cube(),
|
||||
},
|
||||
|pumpkin_block| pumpkin_block.is_pathfindable(state, computation_type),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
use crate::entity::ai::control::Control;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::rotate_if_necessary;
|
||||
|
||||
pub struct BodyRotationControl {
|
||||
head_stable_time: i32,
|
||||
last_stable_y_head_rot: f32,
|
||||
}
|
||||
|
||||
impl Default for BodyRotationControl {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyRotationControl {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
head_stable_time: 0,
|
||||
last_stable_y_head_rot: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn client_tick(&mut self, mob: &dyn Mob) {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
|
||||
if Self::is_moving(mob) {
|
||||
entity.body_yaw.store(entity.yaw.load());
|
||||
Self::rotate_head_if_necessary(mob);
|
||||
self.last_stable_y_head_rot = entity.head_yaw.load();
|
||||
self.head_stable_time = 0;
|
||||
} else if (entity.head_yaw.load() - self.last_stable_y_head_rot).abs() > 15.0 {
|
||||
self.head_stable_time = 0;
|
||||
self.last_stable_y_head_rot = entity.head_yaw.load();
|
||||
Self::rotate_body_if_necessary(mob);
|
||||
} else {
|
||||
self.head_stable_time += 1;
|
||||
if self.head_stable_time > 10 {
|
||||
self.rotate_head_towards_front(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_body_if_necessary(mob: &dyn Mob) {
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
let max_head_y_rot = mob.get_max_head_rotation();
|
||||
entity.body_yaw.store(rotate_if_necessary(
|
||||
entity.body_yaw.load(),
|
||||
entity.head_yaw.load(),
|
||||
max_head_y_rot,
|
||||
));
|
||||
}
|
||||
|
||||
fn rotate_head_if_necessary(mob: &dyn Mob) {
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
let max_head_y_rot = mob.get_max_head_rotation();
|
||||
entity.head_yaw.store(rotate_if_necessary(
|
||||
entity.head_yaw.load(),
|
||||
entity.body_yaw.load(),
|
||||
max_head_y_rot,
|
||||
));
|
||||
}
|
||||
|
||||
fn rotate_head_towards_front(&self, mob: &dyn Mob) {
|
||||
let time_since_starting = self.head_stable_time - 10;
|
||||
let face_forward_fraction = (time_since_starting as f32 / 10.0).clamp(0.0, 1.0);
|
||||
let angle_remaining = mob.get_max_head_rotation() * (1.0 - face_forward_fraction);
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
entity.body_yaw.store(rotate_if_necessary(
|
||||
entity.body_yaw.load(),
|
||||
entity.head_yaw.load(),
|
||||
angle_remaining,
|
||||
));
|
||||
}
|
||||
|
||||
fn is_moving(mob: &dyn Mob) -> bool {
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
let vel = entity.velocity.load();
|
||||
vel.x * vel.x + vel.z * vel.z > 2.500_000_3e-7
|
||||
}
|
||||
}
|
||||
|
||||
impl Control for BodyRotationControl {}
|
||||
113
crates/pumpkin/src/entity/ai/control/flying_move_control.rs
Normal file
113
crates/pumpkin/src/entity/ai/control/flying_move_control.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use crate::entity::ai::control::move_control::Operation;
|
||||
use crate::entity::ai::control::{Control, MoveControlTrait};
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_data::attributes::Attributes;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
pub struct FlyingMoveControl {
|
||||
pub wanted_x: f64,
|
||||
pub wanted_y: f64,
|
||||
pub wanted_z: f64,
|
||||
pub speed_modifier: f64,
|
||||
pub max_turn: i32,
|
||||
pub hovers_in_place: bool,
|
||||
pub operation: Operation,
|
||||
}
|
||||
|
||||
impl FlyingMoveControl {
|
||||
#[must_use]
|
||||
pub const fn new(max_turn: i32, hovers_in_place: bool) -> Self {
|
||||
Self {
|
||||
wanted_x: 0.0,
|
||||
wanted_y: 0.0,
|
||||
wanted_z: 0.0,
|
||||
speed_modifier: 0.0,
|
||||
max_turn,
|
||||
hovers_in_place,
|
||||
operation: Operation::Wait,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Control for FlyingMoveControl {}
|
||||
|
||||
impl MoveControlTrait for FlyingMoveControl {
|
||||
fn tick(&mut self, mob: &dyn Mob) {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let living_entity = &mob_entity.living_entity;
|
||||
let entity = &living_entity.entity;
|
||||
|
||||
if self.operation == Operation::MoveTo {
|
||||
self.operation = Operation::Wait;
|
||||
entity.set_has_no_gravity(true);
|
||||
|
||||
let pos = entity.pos.load();
|
||||
let xd = self.wanted_x - pos.x;
|
||||
let yd = self.wanted_y - pos.y;
|
||||
let zd = self.wanted_z - pos.z;
|
||||
let dd = xd * xd + yd * yd + zd * zd;
|
||||
|
||||
if dd < 2.5000003E-7 {
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, 0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
let y_rot_d = (zd.atan2(xd).to_degrees() as f32) - 90.0;
|
||||
entity
|
||||
.yaw
|
||||
.store(self.change_angle(entity.yaw.load(), y_rot_d, 90.0));
|
||||
|
||||
let speed = if entity.on_ground.load(Ordering::Relaxed) {
|
||||
let movement_speed = living_entity.get_attribute_value(&Attributes::MOVEMENT_SPEED);
|
||||
(self.speed_modifier * movement_speed) as f32
|
||||
} else {
|
||||
let flying_speed = living_entity.get_attribute_value(&Attributes::FLYING_SPEED);
|
||||
(self.speed_modifier * flying_speed) as f32
|
||||
};
|
||||
|
||||
let sd = xd.hypot(zd);
|
||||
if yd.abs() > 1.0E-5 || sd > 1.0E-5 {
|
||||
let x_rot_d = -((yd.atan2(sd).to_degrees()) as f32);
|
||||
entity.pitch.store(self.change_angle(
|
||||
entity.pitch.load(),
|
||||
x_rot_d,
|
||||
self.max_turn as f32,
|
||||
));
|
||||
let yya = if yd > 0.0 {
|
||||
speed as f64
|
||||
} else {
|
||||
-(speed as f64)
|
||||
};
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, yya, speed as f64));
|
||||
} else {
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, speed as f64));
|
||||
}
|
||||
} else {
|
||||
if !self.hovers_in_place {
|
||||
entity.set_has_no_gravity(false);
|
||||
}
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_wanted_position(&mut self, x: f64, y: f64, z: f64, speed_modifier: f64) {
|
||||
self.wanted_x = x;
|
||||
self.wanted_y = y;
|
||||
self.wanted_z = z;
|
||||
self.speed_modifier = speed_modifier;
|
||||
self.operation = Operation::MoveTo;
|
||||
}
|
||||
|
||||
fn has_wanted(&self) -> bool {
|
||||
self.operation == Operation::MoveTo
|
||||
}
|
||||
}
|
||||
24
crates/pumpkin/src/entity/ai/control/jump_control.rs
Normal file
24
crates/pumpkin/src/entity/ai/control/jump_control.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use crate::entity::ai::control::Control;
|
||||
use crate::entity::mob::Mob;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct JumpControl {
|
||||
jump: bool,
|
||||
}
|
||||
|
||||
impl Control for JumpControl {}
|
||||
|
||||
impl JumpControl {
|
||||
pub const fn jump(&mut self) {
|
||||
self.jump = true;
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, mob: &dyn Mob) {
|
||||
mob.get_mob_entity()
|
||||
.living_entity
|
||||
.jumping
|
||||
.store(self.jump, Ordering::SeqCst);
|
||||
self.jump = false;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,13 @@
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::subtract_angles;
|
||||
|
||||
pub mod body_rotation_control;
|
||||
pub mod flying_move_control;
|
||||
pub mod jump_control;
|
||||
pub mod look_control;
|
||||
pub mod move_control;
|
||||
pub mod smooth_swimming_look_control;
|
||||
pub mod smooth_swimming_move_control;
|
||||
|
||||
pub trait Control: Send + Sync {
|
||||
fn change_angle(&self, start: f32, end: f32, max_change: f32) -> f32 {
|
||||
|
||||
@ -0,0 +1,113 @@
|
||||
use crate::entity::ai::control::Control;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::math::wrap_degrees;
|
||||
|
||||
pub struct SmoothSwimmingLookControl {
|
||||
pub max_yaw_change: f32,
|
||||
pub max_pitch_change: f32,
|
||||
pub look_at_timer: i32,
|
||||
pub position: Vector3<f64>,
|
||||
pub max_y_rot_from_center: i32,
|
||||
}
|
||||
|
||||
impl SmoothSwimmingLookControl {
|
||||
#[must_use]
|
||||
pub const fn new(max_y_rot_from_center: i32) -> Self {
|
||||
Self {
|
||||
max_yaw_change: 0.0,
|
||||
max_pitch_change: 0.0,
|
||||
look_at_timer: 0,
|
||||
position: Vector3::new(0.0, 0.0, 0.0),
|
||||
max_y_rot_from_center,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn look_at_with_range(
|
||||
&mut self,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
max_yaw_change: f32,
|
||||
max_pitch_change: f32,
|
||||
) {
|
||||
self.position = Vector3::new(x, y, z);
|
||||
self.max_yaw_change = max_yaw_change;
|
||||
self.max_pitch_change = max_pitch_change;
|
||||
self.look_at_timer = 2;
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, mob: &dyn Mob) {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
|
||||
if self.look_at_timer > 0 {
|
||||
self.look_at_timer -= 1;
|
||||
if let Some(yaw) = self.get_target_yaw(mob) {
|
||||
entity.head_yaw.store(self.change_angle(
|
||||
entity.head_yaw.load(),
|
||||
yaw + 20.0,
|
||||
self.max_yaw_change,
|
||||
));
|
||||
}
|
||||
if let Some(pitch) = self.get_target_pitch(mob) {
|
||||
entity.set_pitch(self.change_angle(
|
||||
entity.pitch.load(),
|
||||
pitch + 10.0,
|
||||
self.max_pitch_change,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let is_idle = mob_entity
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|navigator| navigator.is_idle());
|
||||
if is_idle {
|
||||
entity.set_pitch(self.change_angle(entity.pitch.load(), 0.0, 5.0));
|
||||
}
|
||||
entity.head_yaw.store(self.change_angle(
|
||||
entity.head_yaw.load(),
|
||||
entity.body_yaw.load(),
|
||||
self.max_yaw_change,
|
||||
));
|
||||
}
|
||||
|
||||
let head_diff_body = wrap_degrees(entity.head_yaw.load() - entity.body_yaw.load());
|
||||
let max_rot = self.max_y_rot_from_center as f32;
|
||||
if head_diff_body < -max_rot {
|
||||
let body_yaw = entity.body_yaw.load();
|
||||
entity.body_yaw.store(body_yaw - 4.0);
|
||||
} else if head_diff_body > max_rot {
|
||||
let body_yaw = entity.body_yaw.load();
|
||||
entity.body_yaw.store(body_yaw + 4.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_target_pitch(&self, mob: &dyn Mob) -> Option<f32> {
|
||||
let position = self.position;
|
||||
let mob_position = mob.get_entity().pos.load();
|
||||
let d = position.x - mob_position.x;
|
||||
let e = position.y - mob.get_entity().get_eye_y();
|
||||
let f = position.z - mob_position.z;
|
||||
let g = d.hypot(f);
|
||||
if e.abs() <= 1.0E-5 && g.abs() <= 1.0E-5 {
|
||||
None
|
||||
} else {
|
||||
Some(-(e.atan2(g) as f32).to_degrees())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_target_yaw(&self, mob: &dyn Mob) -> Option<f32> {
|
||||
let position = self.position;
|
||||
let mob_position = mob.get_entity().pos.load();
|
||||
let d = position.x - mob_position.x;
|
||||
let e = position.z - mob_position.z;
|
||||
if e.abs() <= 1.0E-5 && d.abs() <= 1.0E-5 {
|
||||
None
|
||||
} else {
|
||||
Some((e.atan2(d) as f32).to_degrees() - 90.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Control for SmoothSwimmingLookControl {}
|
||||
@ -0,0 +1,138 @@
|
||||
use crate::entity::ai::control::move_control::Operation;
|
||||
use crate::entity::ai::control::{Control, MoveControlTrait};
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_data::attributes::Attributes;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_util::math::wrap_degrees;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
pub struct SmoothSwimmingMoveControl {
|
||||
pub wanted_x: f64,
|
||||
pub wanted_y: f64,
|
||||
pub wanted_z: f64,
|
||||
pub speed_modifier: f64,
|
||||
pub max_turn_x: i32,
|
||||
pub max_turn_y: i32,
|
||||
pub in_water_speed_modifier: f32,
|
||||
pub outside_water_speed_modifier: f32,
|
||||
pub apply_gravity: bool,
|
||||
pub operation: Operation,
|
||||
}
|
||||
|
||||
impl SmoothSwimmingMoveControl {
|
||||
#[must_use]
|
||||
pub const fn new(
|
||||
max_turn_x: i32,
|
||||
max_turn_y: i32,
|
||||
in_water_speed_modifier: f32,
|
||||
outside_water_speed_modifier: f32,
|
||||
apply_gravity: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
wanted_x: 0.0,
|
||||
wanted_y: 0.0,
|
||||
wanted_z: 0.0,
|
||||
speed_modifier: 0.0,
|
||||
max_turn_x,
|
||||
max_turn_y,
|
||||
in_water_speed_modifier,
|
||||
outside_water_speed_modifier,
|
||||
apply_gravity,
|
||||
operation: Operation::Wait,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_turning_speed_factor(left_to_turn: f32) -> f32 {
|
||||
1.0 - ((left_to_turn - 10.0) / 50.0).clamp(0.0, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Control for SmoothSwimmingMoveControl {}
|
||||
|
||||
impl MoveControlTrait for SmoothSwimmingMoveControl {
|
||||
fn tick(&mut self, mob: &dyn Mob) {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let living_entity = &mob_entity.living_entity;
|
||||
let entity = &living_entity.entity;
|
||||
|
||||
if self.apply_gravity && entity.touching_water.load(Ordering::Relaxed) {
|
||||
let vel = entity.velocity.load();
|
||||
entity.set_velocity(Vector3::new(vel.x, vel.y + 0.005, vel.z));
|
||||
}
|
||||
|
||||
let is_idle = mob_entity
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|navigator| navigator.is_idle());
|
||||
|
||||
if self.operation == Operation::MoveTo && !is_idle {
|
||||
let pos = entity.pos.load();
|
||||
let xd = self.wanted_x - pos.x;
|
||||
let yd = self.wanted_y - pos.y;
|
||||
let zd = self.wanted_z - pos.z;
|
||||
let dd = xd * xd + yd * yd + zd * zd;
|
||||
|
||||
if dd < 2.5000003E-7 {
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, 0.0));
|
||||
return;
|
||||
}
|
||||
|
||||
let y_rot_d = (zd.atan2(xd).to_degrees() as f32) - 90.0;
|
||||
let current_yaw = entity.yaw.load();
|
||||
let new_yaw = self.change_angle(current_yaw, y_rot_d, self.max_turn_y as f32);
|
||||
entity.yaw.store(new_yaw);
|
||||
entity.body_yaw.store(new_yaw);
|
||||
entity.head_yaw.store(new_yaw);
|
||||
|
||||
let movement_speed = living_entity.get_attribute_value(&Attributes::MOVEMENT_SPEED);
|
||||
let speed = (self.speed_modifier * movement_speed) as f32;
|
||||
|
||||
if entity.touching_water.load(Ordering::Relaxed) {
|
||||
let water_speed = speed * self.in_water_speed_modifier;
|
||||
let sqrt = xd.hypot(zd);
|
||||
if yd.abs() > 1.0E-5 || sqrt > 1.0E-5 {
|
||||
let mut x_rot_d = -((yd.atan2(sqrt).to_degrees()) as f32);
|
||||
x_rot_d = wrap_degrees(x_rot_d)
|
||||
.clamp(-(self.max_turn_x as f32), self.max_turn_x as f32);
|
||||
entity
|
||||
.pitch
|
||||
.store(self.change_angle(entity.pitch.load(), x_rot_d, 5.0));
|
||||
}
|
||||
|
||||
let pitch_rad = entity.pitch.load().to_radians();
|
||||
let cos = pitch_rad.cos();
|
||||
let sin = pitch_rad.sin();
|
||||
living_entity.movement_input.store(Vector3::new(
|
||||
0.0,
|
||||
-(sin * water_speed) as f64,
|
||||
(cos * water_speed) as f64,
|
||||
));
|
||||
} else {
|
||||
let left_to_turn = wrap_degrees(entity.yaw.load() - y_rot_d).abs();
|
||||
let factor = Self::get_turning_speed_factor(left_to_turn);
|
||||
let land_speed = speed * self.outside_water_speed_modifier * factor;
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, land_speed as f64));
|
||||
}
|
||||
} else {
|
||||
living_entity
|
||||
.movement_input
|
||||
.store(Vector3::new(0.0, 0.0, 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_wanted_position(&mut self, x: f64, y: f64, z: f64, speed_modifier: f64) {
|
||||
self.wanted_x = x;
|
||||
self.wanted_y = y;
|
||||
self.wanted_z = z;
|
||||
self.speed_modifier = speed_modifier;
|
||||
self.operation = Operation::MoveTo;
|
||||
}
|
||||
|
||||
fn has_wanted(&self) -> bool {
|
||||
self.operation == Operation::MoveTo
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
use pumpkin_protocol::java::client::play::CWorldEvent;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::entity::{
|
||||
@ -30,7 +28,6 @@ impl BlazeShootFireballGoal {
|
||||
}
|
||||
|
||||
const fn get_follow_distance() -> f64 {
|
||||
// TODO: use FOLLOW_RANGE
|
||||
48.0
|
||||
}
|
||||
}
|
||||
@ -41,12 +38,7 @@ impl Goal for BlazeShootFireballGoal {
|
||||
return false;
|
||||
};
|
||||
let target = blaze.entity.get_target();
|
||||
if target.is_some() {
|
||||
// TODO: check is_alive
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
target.is_some_and(|t| t.get_entity().is_alive())
|
||||
}
|
||||
|
||||
fn should_continue(&self, _mob: &dyn Mob) -> bool {
|
||||
@ -54,12 +46,7 @@ impl Goal for BlazeShootFireballGoal {
|
||||
return false;
|
||||
};
|
||||
let target = blaze.entity.get_target();
|
||||
if target.is_some() {
|
||||
// TODO: check is_alive
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
target.is_some_and(|t| t.get_entity().is_alive())
|
||||
}
|
||||
|
||||
fn start(&mut self, _mob: &dyn Mob) {
|
||||
@ -89,7 +76,6 @@ impl Goal for BlazeShootFireballGoal {
|
||||
return;
|
||||
};
|
||||
|
||||
// TODO: hasLineOfSight check
|
||||
let has_line_of_sight = true;
|
||||
|
||||
if has_line_of_sight {
|
||||
@ -114,15 +100,9 @@ impl Goal for BlazeShootFireballGoal {
|
||||
|
||||
if self.attack_time <= 0 {
|
||||
self.attack_time = 20;
|
||||
// TODO: doHurtTarget
|
||||
blaze.entity.try_attack(&*blaze, &*target);
|
||||
}
|
||||
|
||||
// TODO: set wanted position to target
|
||||
} else if distance_sq < Self::get_follow_distance().powi(2) && has_line_of_sight {
|
||||
let target_y_offset = target_pos.y + 0.5; // roughly target.getY(0.5)
|
||||
let blaze_y_offset = blaze_pos.y + 0.5; // roughly blaze.getY(0.5)
|
||||
let yd = target_y_offset - blaze_y_offset;
|
||||
|
||||
if self.attack_time <= 0 {
|
||||
self.attack_step += 1;
|
||||
if self.attack_step == 1 {
|
||||
@ -137,9 +117,6 @@ impl Goal for BlazeShootFireballGoal {
|
||||
}
|
||||
|
||||
if self.attack_step > 1 {
|
||||
let distance = distance_sq.sqrt();
|
||||
let sqd = distance.sqrt() * 0.5;
|
||||
// play shoot sound
|
||||
let chunk_pos = blaze.entity.living_entity.entity.chunk_pos.load();
|
||||
blaze
|
||||
.entity
|
||||
@ -157,50 +134,33 @@ impl Goal for BlazeShootFireballGoal {
|
||||
),
|
||||
);
|
||||
|
||||
for _ in 0..1 {
|
||||
// Vanilla loops 1 time
|
||||
// Calculate spread
|
||||
let _direction = {
|
||||
let mut rng = rand::rng();
|
||||
let dir_x = (dx - 2.297 * sqd)
|
||||
+ rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0);
|
||||
let dir_z = (dz - 2.297 * sqd)
|
||||
+ rng.random_range(0.0..1.0) * (2.297 * sqd * 2.0);
|
||||
Vector3::new(dir_x, yd, dir_z).normalize()
|
||||
};
|
||||
let world = blaze.entity.living_entity.entity.world.load_full();
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
|
||||
// Spawn SmallFireball
|
||||
let world = blaze.entity.living_entity.entity.world.load_full();
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
let mut pos = blaze.entity.living_entity.entity.pos.load();
|
||||
pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1;
|
||||
|
||||
let mut pos = blaze.entity.living_entity.entity.pos.load();
|
||||
pos.y += blaze.entity.living_entity.entity.get_eye_height() - 0.1;
|
||||
let base_entity = Entity::from_uuid(
|
||||
uuid,
|
||||
world.clone(),
|
||||
pos,
|
||||
&pumpkin_data::entity::EntityType::SMALL_FIREBALL,
|
||||
);
|
||||
|
||||
let base_entity = Entity::from_uuid(
|
||||
uuid,
|
||||
world.clone(),
|
||||
pos,
|
||||
&pumpkin_data::entity::EntityType::SMALL_FIREBALL,
|
||||
);
|
||||
|
||||
let fireball = SmallFireballEntity::new_shot(
|
||||
base_entity,
|
||||
&blaze.entity.living_entity.entity,
|
||||
);
|
||||
world.spawn_entity(Arc::new(fireball));
|
||||
}
|
||||
let fireball = SmallFireballEntity::new_shot(
|
||||
base_entity,
|
||||
&blaze.entity.living_entity.entity,
|
||||
);
|
||||
world.spawn_entity(Arc::new(fireball));
|
||||
}
|
||||
}
|
||||
|
||||
// Look at target
|
||||
blaze
|
||||
.entity
|
||||
.look_control
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.look_at_entity(&*blaze, &target);
|
||||
} else if self.last_seen < 5 {
|
||||
// TODO: set wanted position to target
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -178,42 +178,3 @@ impl Goal for BreakDoorGoal {
|
||||
Controls::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn door_break_time_default_and_custom() {
|
||||
let default_goal = BreakDoorGoal::default();
|
||||
assert_eq!(default_goal.get_door_break_time(), 240);
|
||||
|
||||
let custom_goal =
|
||||
BreakDoorGoal::with_door_break_time(300, Arc::new(|d| d == Difficulty::Hard));
|
||||
assert_eq!(custom_goal.get_door_break_time(), 300);
|
||||
|
||||
let low_goal =
|
||||
BreakDoorGoal::with_door_break_time(100, Arc::new(|d| d == Difficulty::Hard));
|
||||
assert_eq!(low_goal.get_door_break_time(), 240);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_difficulty() {
|
||||
let hard_only = BreakDoorGoal::new(Arc::new(|d| d == Difficulty::Hard));
|
||||
assert!(hard_only.is_valid_difficulty(Difficulty::Hard));
|
||||
assert!(!hard_only.is_valid_difficulty(Difficulty::Normal));
|
||||
assert!(!hard_only.is_valid_difficulty(Difficulty::Easy));
|
||||
assert!(!hard_only.is_valid_difficulty(Difficulty::Peaceful));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn break_progress_calculation() {
|
||||
let break_time = 120;
|
||||
let total_time = 240;
|
||||
let progress = (break_time as f32 / total_time as f32 * 10.0) as i32;
|
||||
assert_eq!(progress, 5);
|
||||
|
||||
let end_progress = (240.0f32 / 240.0f32 * 10.0) as i32;
|
||||
assert_eq!(end_progress, 10);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,44 +154,3 @@ impl Goal for DoorInteractGoal {
|
||||
Controls::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn passed_when_crossing_door() {
|
||||
let mut goal = DoorInteractGoal::new();
|
||||
goal.door_pos = BlockPos::new(10, 64, 10);
|
||||
|
||||
// Simulate start: mob at (10.5, 64.0, 8.0), moving towards door at Z=10.5
|
||||
// door_open_dir = (10.5 - 10.5, 10.5 - 8.0) = (0.0, 2.5)
|
||||
goal.door_open_dir_x = 0.0;
|
||||
goal.door_open_dir_z = 2.5;
|
||||
goal.passed = false;
|
||||
|
||||
assert!(goal.can_continue_to_use());
|
||||
|
||||
// Still before door: mob at (10.5, 64.0, 9.5) -> new_door_dir = (0.0, 1.0)
|
||||
// dot = 0*0 + 2.5*1.0 = 2.5 > 0
|
||||
let new_x = 0.0f32;
|
||||
let new_z = 1.0f32;
|
||||
let dot = goal.door_open_dir_x * new_x + goal.door_open_dir_z * new_z;
|
||||
if dot < 0.0 {
|
||||
goal.passed = true;
|
||||
}
|
||||
assert!(!goal.passed);
|
||||
assert!(goal.can_continue_to_use());
|
||||
|
||||
// Crossed door: mob at (10.5, 64.0, 12.0) -> new_door_dir = (0.0, -1.5)
|
||||
// dot = 0*0 + 2.5*(-1.5) = -3.75 < 0
|
||||
let new_x2 = 0.0f32;
|
||||
let new_z2 = -1.5f32;
|
||||
let dot2 = goal.door_open_dir_x * new_x2 + goal.door_open_dir_z * new_z2;
|
||||
if dot2 < 0.0 {
|
||||
goal.passed = true;
|
||||
}
|
||||
assert!(goal.passed);
|
||||
assert!(!goal.can_continue_to_use());
|
||||
}
|
||||
}
|
||||
|
||||
118
crates/pumpkin/src/entity/ai/goal/flee_sun.rs
Normal file
118
crates/pumpkin/src/entity/ai/goal/flee_sun.rs
Normal file
@ -0,0 +1,118 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::ai::pathfinder::NavigatorGoal;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
|
||||
pub struct FleeSunGoal {
|
||||
goal_control: Controls,
|
||||
speed_modifier: f64,
|
||||
wanted_x: f64,
|
||||
wanted_y: f64,
|
||||
wanted_z: f64,
|
||||
}
|
||||
|
||||
impl FleeSunGoal {
|
||||
#[must_use]
|
||||
pub const fn new(speed_modifier: f64) -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE,
|
||||
speed_modifier,
|
||||
wanted_x: 0.0,
|
||||
wanted_y: 0.0,
|
||||
wanted_z: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_hide_pos(mob: &dyn Mob) -> Option<Vector3<f64>> {
|
||||
let mob_pos = mob.get_entity().block_pos.load();
|
||||
let world = mob.get_entity().world.load();
|
||||
let mut rng = mob.get_random();
|
||||
|
||||
for _ in 0..10 {
|
||||
let offset_x = rng.random_range(-10..=10);
|
||||
let offset_y = rng.random_range(-3..=3);
|
||||
let offset_z = rng.random_range(-10..=10);
|
||||
let check_pos = BlockPos::new(
|
||||
mob_pos.0.x + offset_x,
|
||||
mob_pos.0.y + offset_y,
|
||||
mob_pos.0.z + offset_z,
|
||||
);
|
||||
|
||||
let block_at = world.get_block_state(&check_pos);
|
||||
let block_above = world.get_block_state(&check_pos.up());
|
||||
let block_below = world.get_block_state(&check_pos.down());
|
||||
|
||||
if !block_at.is_solid() && !block_above.is_solid() && block_below.is_solid() {
|
||||
return Some(Vector3::new(
|
||||
f64::from(check_pos.0.x) + 0.5,
|
||||
f64::from(check_pos.0.y),
|
||||
f64::from(check_pos.0.z) + 0.5,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for FleeSunGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
if mob.get_mob_entity().get_target().is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !mob.get_mob_entity().living_entity.entity.is_on_fire() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let has_helmet = mob
|
||||
.get_mob_entity()
|
||||
.living_entity
|
||||
.entity_equipment
|
||||
.try_lock()
|
||||
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
|
||||
|
||||
if has_helmet {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(pos) = Self::find_hide_pos(mob) {
|
||||
self.wanted_x = pos.x;
|
||||
self.wanted_y = pos.y;
|
||||
self.wanted_z = pos.z;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let is_idle = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|nav| nav.is_idle());
|
||||
!is_idle
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.set_progress(NavigatorGoal::new(
|
||||
mob_pos,
|
||||
Vector3::new(self.wanted_x, self.wanted_y, self.wanted_z),
|
||||
self.speed_modifier,
|
||||
));
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
131
crates/pumpkin/src/entity/ai/goal/follow_mob.rs
Normal file
131
crates/pumpkin/src/entity/ai/goal/follow_mob.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use super::{Controls, Goal, to_goal_ticks};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::ai::pathfinder::NavigatorGoal;
|
||||
use crate::entity::mob::Mob;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct FollowMobGoal {
|
||||
goal_control: Controls,
|
||||
following_mob: Option<Arc<dyn EntityBase>>,
|
||||
speed_modifier: f64,
|
||||
time_to_recalc_path: i32,
|
||||
stop_distance_sq: f64,
|
||||
area_size: f64,
|
||||
}
|
||||
|
||||
impl FollowMobGoal {
|
||||
#[must_use]
|
||||
pub fn new(speed_modifier: f64, stop_distance: f32, area_size: f32) -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE | Controls::LOOK,
|
||||
following_mob: None,
|
||||
speed_modifier,
|
||||
time_to_recalc_path: 0,
|
||||
stop_distance_sq: f64::from(stop_distance * stop_distance),
|
||||
area_size: f64::from(area_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for FollowMobGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
let my_type = mob.get_entity().entity_type;
|
||||
let my_uuid = mob.get_entity().entity_uuid;
|
||||
let pos = mob.get_entity().pos.load();
|
||||
let world = mob.get_entity().world.load();
|
||||
|
||||
let nearby = world.get_nearby_entities(pos, self.area_size);
|
||||
for candidate in nearby.values() {
|
||||
let c_entity = candidate.get_entity();
|
||||
if c_entity.entity_uuid == my_uuid {
|
||||
continue;
|
||||
}
|
||||
if c_entity.entity_type == my_type {
|
||||
continue;
|
||||
}
|
||||
if candidate.get_living_entity().is_some() {
|
||||
self.following_mob = Some(candidate.clone());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let Some(following) = &self.following_mob else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !following.get_entity().is_alive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_idle = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|nav| nav.is_idle());
|
||||
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let follow_pos = following.get_entity().pos.load();
|
||||
let dist_sq = mob_pos.squared_distance_to_vec(&follow_pos);
|
||||
|
||||
!is_idle && dist_sq > self.stop_distance_sq
|
||||
}
|
||||
|
||||
fn start(&mut self, _mob: &dyn Mob) {
|
||||
self.time_to_recalc_path = 0;
|
||||
}
|
||||
|
||||
fn stop(&mut self, mob: &dyn Mob) {
|
||||
self.following_mob = None;
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.stop();
|
||||
}
|
||||
|
||||
fn tick(&mut self, mob: &dyn Mob) {
|
||||
let Some(following) = &self.following_mob else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let follow_pos = following.get_entity().pos.load();
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
|
||||
mob_entity
|
||||
.look_control
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.look_at_entity(mob, following);
|
||||
|
||||
self.time_to_recalc_path -= 1;
|
||||
if self.time_to_recalc_path <= 0 {
|
||||
self.time_to_recalc_path = to_goal_ticks(10);
|
||||
let dist_sq = mob_pos.squared_distance_to_vec(&follow_pos);
|
||||
|
||||
let mut navigator = mob_entity
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
|
||||
if dist_sq > self.stop_distance_sq {
|
||||
navigator.set_progress(NavigatorGoal::new(
|
||||
mob_pos,
|
||||
follow_pos,
|
||||
self.speed_modifier,
|
||||
));
|
||||
} else {
|
||||
navigator.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
53
crates/pumpkin/src/entity/ai/goal/interact.rs
Normal file
53
crates/pumpkin/src/entity/ai/goal/interact.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use super::look_at_entity::LookAtEntityGoal;
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use std::sync::Weak;
|
||||
|
||||
pub struct InteractGoal {
|
||||
look_at_goal: LookAtEntityGoal,
|
||||
}
|
||||
|
||||
impl InteractGoal {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
mob_weak: Weak<dyn Mob>,
|
||||
target_type: &'static EntityType,
|
||||
range: f32,
|
||||
chance: f32,
|
||||
) -> Self {
|
||||
Self {
|
||||
look_at_goal: LookAtEntityGoal::new(mob_weak, target_type, range, chance, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for InteractGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
self.look_at_goal.can_start(mob)
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
self.look_at_goal.should_continue(mob)
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
self.look_at_goal.start(mob);
|
||||
}
|
||||
|
||||
fn stop(&mut self, mob: &dyn Mob) {
|
||||
self.look_at_goal.stop(mob);
|
||||
}
|
||||
|
||||
fn tick(&mut self, mob: &dyn Mob) {
|
||||
self.look_at_goal.tick(mob);
|
||||
}
|
||||
|
||||
fn should_run_every_tick(&self) -> bool {
|
||||
self.look_at_goal.should_run_every_tick()
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
Controls::LOOK | Controls::MOVE
|
||||
}
|
||||
}
|
||||
101
crates/pumpkin/src/entity/ai/goal/leap_at_target.rs
Normal file
101
crates/pumpkin/src/entity/ai/goal/leap_at_target.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use super::{Controls, Goal, to_goal_ticks};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
pub struct LeapAtTargetGoal {
|
||||
goal_control: Controls,
|
||||
yd: f32,
|
||||
target: Option<Arc<dyn EntityBase>>,
|
||||
}
|
||||
|
||||
impl LeapAtTargetGoal {
|
||||
#[must_use]
|
||||
pub const fn new(yd: f32) -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE.union(Controls::JUMP),
|
||||
yd,
|
||||
target: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for LeapAtTargetGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
|
||||
if !entity.on_ground.load(Ordering::Relaxed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let target = mob_entity.get_target();
|
||||
let Some(target) = target else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !target.get_entity().is_alive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mob_pos = entity.pos.load();
|
||||
let target_pos = target.get_entity().pos.load();
|
||||
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
|
||||
|
||||
if !(4.0..=16.0).contains(&dist_sq) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if mob.get_random().random_range(0..to_goal_ticks(5).max(1)) != 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.target = Some(target);
|
||||
true
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
!entity.on_ground.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
let Some(target) = &self.target else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let entity = &mob_entity.living_entity.entity;
|
||||
let mob_pos = entity.pos.load();
|
||||
let target_pos = target.get_entity().pos.load();
|
||||
let movement = entity.velocity.load();
|
||||
|
||||
let dx = target_pos.x - mob_pos.x;
|
||||
let dz = target_pos.z - mob_pos.z;
|
||||
let dist_xz_sq = dx * dx + dz * dz;
|
||||
|
||||
let delta = if dist_xz_sq > 1.0E-7 {
|
||||
let dist_xz = dist_xz_sq.sqrt();
|
||||
Vector3::new(
|
||||
(dx / dist_xz) * 0.4 + movement.x * 0.2,
|
||||
f64::from(self.yd),
|
||||
(dz / dist_xz) * 0.4 + movement.z * 0.2,
|
||||
)
|
||||
} else {
|
||||
Vector3::new(movement.x, f64::from(self.yd), movement.z)
|
||||
};
|
||||
|
||||
entity.set_velocity(delta);
|
||||
}
|
||||
|
||||
fn stop(&mut self, _mob: &dyn Mob) {
|
||||
self.target = None;
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
@ -15,14 +15,20 @@ pub mod destroy_egg;
|
||||
pub mod door_interact;
|
||||
pub mod eat_grass;
|
||||
pub mod escape_danger;
|
||||
pub mod flee_sun;
|
||||
pub mod follow_mob;
|
||||
pub mod follow_owner;
|
||||
pub mod follow_parent;
|
||||
pub mod goal_selector;
|
||||
pub mod interact;
|
||||
pub mod leap_at_target;
|
||||
pub mod look_around;
|
||||
pub mod look_at_entity;
|
||||
pub mod melee_attack;
|
||||
pub mod move_to_target_pos;
|
||||
pub mod move_towards_restriction;
|
||||
pub mod move_towards_target;
|
||||
pub mod ocelot_attack;
|
||||
pub mod offer_flower;
|
||||
pub mod open_door;
|
||||
pub mod owner_hurt_by_target;
|
||||
@ -32,7 +38,10 @@ pub mod pick_up_block;
|
||||
pub mod place_block;
|
||||
pub mod ranged_attack;
|
||||
pub mod ranged_crossbow_attack;
|
||||
pub mod restrict_sun;
|
||||
pub mod revenge;
|
||||
pub mod run_around_like_crazy;
|
||||
pub mod sit_when_ordered_to;
|
||||
pub mod step_and_destroy_block;
|
||||
pub mod swim;
|
||||
pub mod teleport_towards_player;
|
||||
@ -40,7 +49,9 @@ pub mod tempt;
|
||||
pub(crate) mod track_target;
|
||||
pub mod trade_with_player;
|
||||
pub mod try_find_water;
|
||||
pub mod use_item;
|
||||
pub mod wander_around;
|
||||
pub mod water_avoiding_random_flying;
|
||||
pub mod work_at_job_site;
|
||||
pub mod zombie_attack;
|
||||
|
||||
@ -138,6 +149,11 @@ impl Controls {
|
||||
(self.0 & control.0) != 0
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn union(self, other: Self) -> Self {
|
||||
Self(self.0 | other.0)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn idx(&self) -> usize {
|
||||
self.0.trailing_zeros() as usize
|
||||
|
||||
114
crates/pumpkin/src/entity/ai/goal/move_towards_restriction.rs
Normal file
114
crates/pumpkin/src/entity/ai/goal/move_towards_restriction.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::ai::pathfinder::NavigatorGoal;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
|
||||
pub struct MoveTowardsRestrictionGoal {
|
||||
goal_control: Controls,
|
||||
speed_modifier: f64,
|
||||
wanted_x: f64,
|
||||
wanted_y: f64,
|
||||
wanted_z: f64,
|
||||
}
|
||||
|
||||
impl MoveTowardsRestrictionGoal {
|
||||
#[must_use]
|
||||
pub const fn new(speed_modifier: f64) -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE,
|
||||
speed_modifier,
|
||||
wanted_x: 0.0,
|
||||
wanted_y: 0.0,
|
||||
wanted_z: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_pos_towards(mob: &dyn Mob, target_pos: BlockPos) -> Option<Vector3<f64>> {
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let world = mob.get_entity().world.load();
|
||||
let mut rng = mob.get_random();
|
||||
|
||||
let dx = f64::from(target_pos.0.x) + 0.5 - mob_pos.x;
|
||||
let dz = f64::from(target_pos.0.z) + 0.5 - mob_pos.z;
|
||||
let base_angle = dz.atan2(dx) - std::f64::consts::FRAC_PI_2;
|
||||
|
||||
for _ in 0..10 {
|
||||
let angle =
|
||||
base_angle + (2.0 * rng.random_range(0.0..1.0) - 1.0) * std::f64::consts::FRAC_PI_2;
|
||||
let t = rng.random_range(0.0..1.0f64).sqrt();
|
||||
let dist = t * 16.0 * std::f64::consts::SQRT_2;
|
||||
let step_x = -dist * angle.sin();
|
||||
let step_z = dist * angle.cos();
|
||||
let step_y = rng.random_range(-7..=7);
|
||||
|
||||
let candidate = BlockPos::new(
|
||||
(mob_pos.x + step_x) as i32,
|
||||
(mob_pos.y + step_y as f64) as i32,
|
||||
(mob_pos.z + step_z) as i32,
|
||||
);
|
||||
|
||||
let block_at = world.get_block_state(&candidate);
|
||||
let block_below = world.get_block_state(&candidate.down());
|
||||
|
||||
if !block_at.is_solid() && block_below.is_solid() {
|
||||
return Some(Vector3::new(
|
||||
f64::from(candidate.0.x) + 0.5,
|
||||
f64::from(candidate.0.y),
|
||||
f64::from(candidate.0.z) + 0.5,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for MoveTowardsRestrictionGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
let block_pos = mob.get_entity().block_pos.load();
|
||||
|
||||
if mob_entity.is_in_position_target_range_pos(&block_pos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let home_pos = mob_entity.position_target.load();
|
||||
let Some(pos) = Self::find_pos_towards(mob, home_pos) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
self.wanted_x = pos.x;
|
||||
self.wanted_y = pos.y;
|
||||
self.wanted_z = pos.z;
|
||||
true
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let is_idle = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|nav| nav.is_idle());
|
||||
!is_idle
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.set_progress(NavigatorGoal::new(
|
||||
mob_pos,
|
||||
Vector3::new(self.wanted_x, self.wanted_y, self.wanted_z),
|
||||
self.speed_modifier,
|
||||
));
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
@ -172,37 +172,3 @@ impl Goal for MoveTowardsTargetGoal {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unimplemented)]
|
||||
mod tests {
|
||||
use super::MoveTowardsTargetGoal;
|
||||
use crate::entity::ai::goal::{Controls, Goal};
|
||||
|
||||
#[test]
|
||||
fn initial_controls_and_state() {
|
||||
let mut goal = MoveTowardsTargetGoal::new(1.0, 16.0);
|
||||
assert_eq!(goal.controls(), Controls::MOVE);
|
||||
assert!(goal.target.is_none());
|
||||
assert!(goal.wanted_pos.is_none());
|
||||
|
||||
goal.wanted_pos = Some(pumpkin_util::math::vector3::Vector3::new(1.0, 2.0, 3.0));
|
||||
assert!(goal.wanted_pos.is_some());
|
||||
goal.stop(&MockMob);
|
||||
assert!(goal.target.is_none());
|
||||
assert!(goal.wanted_pos.is_none());
|
||||
}
|
||||
|
||||
struct MockMob;
|
||||
impl crate::entity::mob::Mob for MockMob {
|
||||
fn get_mob_entity(&self) -> &crate::entity::mob::MobEntity {
|
||||
unimplemented!()
|
||||
}
|
||||
fn mob_write_nbt(&self, _nbt: &mut pumpkin_nbt::compound::NbtCompound) {
|
||||
unimplemented!()
|
||||
}
|
||||
fn mob_read_nbt(&self, _nbt: &pumpkin_nbt::compound::NbtCompound) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
crates/pumpkin/src/entity/ai/goal/ocelot_attack.rs
Normal file
129
crates/pumpkin/src/entity/ai/goal/ocelot_attack.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::ai::pathfinder::NavigatorGoal;
|
||||
use crate::entity::mob::Mob;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct OcelotAttackGoal {
|
||||
goal_control: Controls,
|
||||
target: Option<Arc<dyn EntityBase>>,
|
||||
attack_time: i32,
|
||||
}
|
||||
|
||||
impl Default for OcelotAttackGoal {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl OcelotAttackGoal {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE.union(Controls::LOOK),
|
||||
target: None,
|
||||
attack_time: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for OcelotAttackGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
let target = mob.get_mob_entity().get_target();
|
||||
let Some(target) = target else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !target.get_entity().is_alive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.target = Some(target);
|
||||
true
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let Some(target) = &self.target else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !target.get_entity().is_alive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let target_pos = target.get_entity().pos.load();
|
||||
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
|
||||
|
||||
if dist_sq > 225.0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_idle = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|nav| nav.is_idle());
|
||||
|
||||
!is_idle || mob.get_mob_entity().get_target().is_some()
|
||||
}
|
||||
|
||||
fn stop(&mut self, mob: &dyn Mob) {
|
||||
self.target = None;
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.stop();
|
||||
}
|
||||
|
||||
fn should_run_every_tick(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn tick(&mut self, mob: &dyn Mob) {
|
||||
let Some(target) = &self.target else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mob_entity = mob.get_mob_entity();
|
||||
mob_entity
|
||||
.look_control
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner)
|
||||
.look_at_entity_with_range(target, 30.0, 30.0);
|
||||
|
||||
let bb_width = mob.get_entity().entity_dimension.load().width;
|
||||
let melee_radius_sq = (bb_width * 2.0) * (bb_width * 2.0);
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let target_pos = target.get_entity().pos.load();
|
||||
let dist_sq = mob_pos.squared_distance_to_vec(&target_pos);
|
||||
|
||||
let speed_modifier = if dist_sq > f64::from(melee_radius_sq) && dist_sq < 16.0 {
|
||||
1.33
|
||||
} else if dist_sq < 225.0 {
|
||||
0.6
|
||||
} else {
|
||||
0.8
|
||||
};
|
||||
|
||||
let mut navigator = mob_entity
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.set_progress(NavigatorGoal::new(mob_pos, target_pos, speed_modifier));
|
||||
|
||||
self.attack_time = (self.attack_time - 1).max(0);
|
||||
if dist_sq <= f64::from(melee_radius_sq) && self.attack_time <= 0 {
|
||||
self.attack_time = 20;
|
||||
mob.get_mob_entity().living_entity.swing_hand();
|
||||
mob.get_mob_entity()
|
||||
.try_attack(mob.get_entity(), target.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
@ -59,31 +59,3 @@ impl Goal for OpenDoorGoal {
|
||||
Controls::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn open_door_can_continue_to_use() {
|
||||
let mut goal = OpenDoorGoal::new(true);
|
||||
goal.forget_time = 20;
|
||||
goal.door_interact_goal.passed = false;
|
||||
|
||||
assert!(goal.close_door);
|
||||
assert!(goal.forget_time > 0);
|
||||
assert!(goal.door_interact_goal.can_continue_to_use());
|
||||
|
||||
// When forget_time expires
|
||||
goal.forget_time = 0;
|
||||
assert!(
|
||||
!(goal.close_door
|
||||
&& goal.forget_time > 0
|
||||
&& goal.door_interact_goal.can_continue_to_use())
|
||||
);
|
||||
|
||||
// When close_door is false
|
||||
let goal_no_close = OpenDoorGoal::new(false);
|
||||
assert!(!goal_no_close.close_door);
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,14 +97,3 @@ impl Goal for OwnerHurtByTargetGoal {
|
||||
Controls::TARGET
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn owner_hurt_by_target_goal_controls() {
|
||||
let goal = OwnerHurtByTargetGoal::new();
|
||||
assert!(goal.controls().get(Controls::TARGET));
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,14 +97,3 @@ impl Goal for OwnerHurtTargetGoal {
|
||||
Controls::TARGET
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn owner_hurt_target_goal_controls() {
|
||||
let goal = OwnerHurtTargetGoal::new();
|
||||
assert!(goal.controls().get(Controls::TARGET));
|
||||
}
|
||||
}
|
||||
|
||||
51
crates/pumpkin/src/entity/ai/goal/restrict_sun.rs
Normal file
51
crates/pumpkin/src/entity/ai/goal/restrict_sun.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_data::data_component_impl::EquipmentSlot;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RestrictSunGoal;
|
||||
|
||||
impl RestrictSunGoal {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for RestrictSunGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
let has_helmet = mob
|
||||
.get_mob_entity()
|
||||
.living_entity
|
||||
.entity_equipment
|
||||
.try_lock()
|
||||
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
|
||||
|
||||
if has_helmet {
|
||||
return false;
|
||||
}
|
||||
|
||||
let time = mob.get_entity().world.load().get_world_age() % 24000;
|
||||
time < 12000
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let has_helmet = mob
|
||||
.get_mob_entity()
|
||||
.living_entity
|
||||
.entity_equipment
|
||||
.try_lock()
|
||||
.is_ok_and(|eq| !eq.get(&EquipmentSlot::HEAD).is_empty());
|
||||
|
||||
if has_helmet {
|
||||
return false;
|
||||
}
|
||||
|
||||
let time = mob.get_entity().world.load().get_world_age() % 24000;
|
||||
time < 12000
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
Controls::empty()
|
||||
}
|
||||
}
|
||||
102
crates/pumpkin/src/entity/ai/goal/run_around_like_crazy.rs
Normal file
102
crates/pumpkin/src/entity/ai/goal/run_around_like_crazy.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::ai::pathfinder::NavigatorGoal;
|
||||
use crate::entity::mob::Mob;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
|
||||
pub struct RunAroundLikeCrazyGoal {
|
||||
goal_control: Controls,
|
||||
speed_modifier: f64,
|
||||
pos_x: f64,
|
||||
pos_y: f64,
|
||||
pos_z: f64,
|
||||
}
|
||||
|
||||
impl RunAroundLikeCrazyGoal {
|
||||
#[must_use]
|
||||
pub const fn new(speed_modifier: f64) -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE,
|
||||
speed_modifier,
|
||||
pos_x: 0.0,
|
||||
pos_y: 0.0,
|
||||
pos_z: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_random_pos(mob: &dyn Mob) -> Option<Vector3<f64>> {
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let world = mob.get_entity().world.load();
|
||||
let mut rng = mob.get_random();
|
||||
|
||||
for _ in 0..10 {
|
||||
let dx = rng.random_range(-5.0..=5.0);
|
||||
let dy = rng.random_range(-4.0..=4.0);
|
||||
let dz = rng.random_range(-5.0..=5.0);
|
||||
|
||||
let check_pos = BlockPos::new(
|
||||
(mob_pos.x + dx) as i32,
|
||||
(mob_pos.y + dy) as i32,
|
||||
(mob_pos.z + dz) as i32,
|
||||
);
|
||||
|
||||
let block = world.get_block_state(&check_pos);
|
||||
let block_below = world.get_block_state(&check_pos.down());
|
||||
if !block.is_solid() && block_below.is_solid() {
|
||||
return Some(Vector3::new(
|
||||
f64::from(check_pos.0.x) + 0.5,
|
||||
f64::from(check_pos.0.y),
|
||||
f64::from(check_pos.0.z) + 0.5,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for RunAroundLikeCrazyGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
if mob.is_tamed() || !mob.get_entity().has_passengers() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let Some(pos) = Self::find_random_pos(mob) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
self.pos_x = pos.x;
|
||||
self.pos_y = pos.y;
|
||||
self.pos_z = pos.z;
|
||||
true
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
let is_idle = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.try_lock()
|
||||
.is_ok_and(|nav| nav.is_idle());
|
||||
|
||||
!mob.is_tamed() && !is_idle && mob.get_entity().has_passengers()
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
let mob_pos = mob.get_entity().pos.load();
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.set_progress(NavigatorGoal::new(
|
||||
mob_pos,
|
||||
Vector3::new(self.pos_x, self.pos_y, self.pos_z),
|
||||
self.speed_modifier,
|
||||
));
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
53
crates/pumpkin/src/entity/ai/goal/sit_when_ordered_to.rs
Normal file
53
crates/pumpkin/src/entity/ai/goal/sit_when_ordered_to.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use super::{Controls, Goal};
|
||||
use crate::entity::mob::Mob;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SitWhenOrderedToGoal {
|
||||
goal_control: Controls,
|
||||
}
|
||||
|
||||
impl SitWhenOrderedToGoal {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
goal_control: Controls::MOVE.union(Controls::JUMP),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Goal for SitWhenOrderedToGoal {
|
||||
fn can_start(&mut self, mob: &dyn Mob) -> bool {
|
||||
if !mob.is_sitting() && !mob.is_tamed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let entity = &mob.get_mob_entity().living_entity.entity;
|
||||
if entity.touching_water.load(Ordering::Relaxed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !entity.on_ground.load(Ordering::Relaxed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mob.is_sitting()
|
||||
}
|
||||
|
||||
fn should_continue(&self, mob: &dyn Mob) -> bool {
|
||||
mob.is_sitting()
|
||||
}
|
||||
|
||||
fn start(&mut self, mob: &dyn Mob) {
|
||||
let mut navigator = mob
|
||||
.get_mob_entity()
|
||||
.navigator
|
||||
.lock()
|
||||
.unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
navigator.stop();
|
||||
}
|
||||
|
||||
fn controls(&self) -> Controls {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
@ -183,28 +183,3 @@ impl Goal for TrackTargetGoal {
|
||||
self.goal_control
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{TrackTargetGoal, to_goal_ticks};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[test]
|
||||
fn forgets_unseen_target_after_vanilla_memory_window() {
|
||||
let goal = TrackTargetGoal::with_default(true);
|
||||
let memory_ticks = to_goal_ticks(goal.max_time_without_visibility);
|
||||
|
||||
for _ in 0..memory_ticks {
|
||||
assert!(goal.remembers_visible_target(false));
|
||||
}
|
||||
assert!(!goal.remembers_visible_target(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seeing_target_resets_unseen_memory() {
|
||||
let goal = TrackTargetGoal::with_default(true);
|
||||
assert!(goal.remembers_visible_target(false));
|
||||
assert!(goal.remembers_visible_target(true));
|
||||
assert_eq!(goal.time_without_visibility.load(Ordering::Relaxed), 0);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user