diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs index cf46471b..238262a2 100755 --- a/azalea-core/src/delta.rs +++ b/azalea-core/src/delta.rs @@ -49,10 +49,6 @@ impl Vec3 { } } - pub fn length_squared(&self) -> f64 { - self.x * self.x + self.y * self.y + self.z * self.z - } - pub fn normalize(&self) -> Vec3 { let length = f64::sqrt(self.x * self.x + self.y * self.y + self.z * self.z); if length < 1e-4 { diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 749a0947..c280a85a 100755 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -8,11 +8,9 @@ use std::{ hash::Hash, io::{Cursor, Write}, ops::{Add, AddAssign, Mul, Rem, Sub}, - str::FromStr, }; use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; -#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::resource_location::ResourceLocation; @@ -28,25 +26,25 @@ macro_rules! vec3_impl { /// Get the distance of this vector to the origin by doing `x^2 + y^2 + /// z^2`. #[inline] - pub fn length_sqr(&self) -> $type { + pub fn length_squared(&self) -> $type { self.x * self.x + self.y * self.y + self.z * self.z } /// Get the squared distance from this position to another position. - /// Equivalent to `(self - other).length_sqr()`. + /// Equivalent to `(self - other).length_squared()`. #[inline] - pub fn distance_to_sqr(&self, other: &Self) -> $type { - (self - other).length_sqr() + pub fn distance_squared_to(&self, other: &Self) -> $type { + (self - other).length_squared() } #[inline] - pub fn horizontal_distance_sqr(&self) -> $type { + pub fn horizontal_distance_squared(&self) -> $type { self.x * self.x + self.z * self.z } #[inline] - pub fn horizontal_distance_to_sqr(&self, other: &Self) -> $type { - (self - other).horizontal_distance_sqr() + pub fn horizontal_distance_squared_to(&self, other: &Self) -> $type { + (self - other).horizontal_distance_squared() } /// Return a new instance of this position with the y coordinate @@ -272,6 +270,46 @@ impl BlockPos { pub fn length_manhattan(&self) -> u32 { (self.x.abs() + self.y.abs() + self.z.abs()) as u32 } + + /// Make a new BlockPos with the lower coordinates for each axis. + /// + /// ``` + /// # use azalea_core::position::BlockPos; + /// assert_eq!( + /// BlockPos::min( + /// &BlockPos::new(1, 20, 300), + /// &BlockPos::new(50, 40, 30), + /// ), + /// BlockPos::new(1, 20, 30), + /// ); + /// ``` + pub fn min(&self, other: &Self) -> Self { + Self { + x: self.x.min(other.x), + y: self.y.min(other.y), + z: self.z.min(other.z), + } + } + + /// Make a new BlockPos with the higher coordinates for each axis. + /// + /// ``` + /// # use azalea_core::position::BlockPos; + /// assert_eq!( + /// BlockPos::max( + /// &BlockPos::new(1, 20, 300), + /// &BlockPos::new(50, 40, 30), + /// ), + /// BlockPos::new(50, 40, 300), + /// ); + /// ``` + pub fn max(&self, other: &Self) -> Self { + Self { + x: self.x.max(other.x), + y: self.y.max(other.y), + z: self.z.max(other.z), + } + } } /// Chunk coordinates are used to represent where a chunk is in the world. You @@ -645,51 +683,6 @@ impl AzaleaWrite for ChunkSectionPos { } } -fn parse_three_values(s: &str) -> Result<[T; 3], &'static str> -where - T: FromStr, - ::Err: fmt::Debug, -{ - let parts = s.split_whitespace().collect::>(); - if parts.len() != 3 { - return Err("Expected three values"); - } - - let x = parts[0].parse().map_err(|_| "Invalid X value")?; - let y = parts[1].parse().map_err(|_| "Invalid Y value")?; - let z = parts[2].parse().map_err(|_| "Invalid Z value")?; - - Ok([x, y, z]) -} - -/// Parses a string in the format "X Y Z" into a BlockPos. -/// -/// The input string should contain three integer values separated by spaces, -/// representing the x, y, and z components of the vector respectively. -/// This can be used to parse user input or from `BlockPos::to_string`. -impl FromStr for BlockPos { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let [x, y, z] = parse_three_values::(s)?; - Ok(BlockPos { x, y, z }) - } -} - -/// Parses a string in the format "X Y Z" into a Vec3. -/// -/// The input string should contain three floating-point values separated by -/// spaces, representing the x, y, and z components of the vector respectively. -/// This can be used to parse user input or from `Vec3::to_string`. -impl FromStr for Vec3 { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let [x, y, z] = parse_three_values::(s)?; - Ok(Vec3 { x, y, z }) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/azalea-physics/src/clip.rs b/azalea-physics/src/clip.rs index b68574b7..f24721c5 100644 --- a/azalea-physics/src/clip.rs +++ b/azalea-physics/src/clip.rs @@ -62,21 +62,15 @@ pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResul context.from, context.to, context, - |context, block_pos| { + |ctx, block_pos| { let block_state = chunk_storage.get_block_state(block_pos).unwrap_or_default(); // TODO: add fluid stuff to this (see getFluidState in vanilla source) - let block_shape = context.block_shape(block_state); - clip_with_interaction_override( - &context.from, - &context.to, - block_pos, - block_shape, - &block_state, - ) + let block_shape = ctx.block_shape(block_state); + clip_with_interaction_override(&ctx.from, &ctx.to, block_pos, block_shape, &block_state) // let block_distance = if let Some(block_hit_result) = - // block_hit_result { context.from.distance_to_sqr(& + // block_hit_result { context.from.distance_squared_to(& // block_hit_result.location) } else { - // f64::MAX + // f64::INFINITY // }; }, |context| { @@ -90,19 +84,6 @@ pub fn clip(chunk_storage: &ChunkStorage, context: ClipContext) -> BlockHitResul ) } -// default BlockHitResult clipWithInteractionOverride(Vec3 world, Vec3 from, -// BlockPos to, VoxelShape shape, BlockState block) { -// BlockHitResult blockHitResult = shape.clip(world, from, to); -// if (blockHitResult != null) { -// BlockHitResult var7 = block.getInteractionShape(this, to).clip(world, -// from, to); if (var7 != null -// && var7.getLocation().subtract(world).lengthSqr() < -// blockHitResult.getLocation().subtract(world).lengthSqr()) { return -// blockHitResult.withDirection(var7.getDirection()); } -// } - -// return blockHitResult; -// } fn clip_with_interaction_override( from: &Vec3, to: &Vec3, @@ -119,8 +100,8 @@ fn clip_with_interaction_override( let interaction_shape = block_state.shape(); let interaction_hit_result = interaction_shape.clip(from, to, block_pos); if let Some(interaction_hit_result) = interaction_hit_result { - if interaction_hit_result.location.distance_to_sqr(from) - < block_hit_result.location.distance_to_sqr(from) + if interaction_hit_result.location.distance_squared_to(from) + < block_hit_result.location.distance_squared_to(from) { return Some(block_hit_result.with_direction(interaction_hit_result.direction)); } diff --git a/azalea-physics/src/collision/mod.rs b/azalea-physics/src/collision/mod.rs index 3986dc47..7d7ddc5e 100644 --- a/azalea-physics/src/collision/mod.rs +++ b/azalea-physics/src/collision/mod.rs @@ -58,7 +58,7 @@ fn collide(movement: &Vec3, world: &Instance, physics: &azalea_entity::Physics) // let entity_collisions = world.get_entity_collisions(self, // entity_bounding_box.expand_towards(movement)); let entity_collisions = Vec::new(); - let collided_delta = if movement.length_sqr() == 0.0 { + let collided_delta = if movement.length_squared() == 0.0 { *movement } else { collide_bounding_box( @@ -109,12 +109,16 @@ fn collide(movement: &Vec3, world: &Instance, physics: &azalea_entity::Physics) entity_collisions.clone(), ) .add(directly_up_delta); - if target_movement.horizontal_distance_sqr() > step_to_delta.horizontal_distance_sqr() { + if target_movement.horizontal_distance_squared() + > step_to_delta.horizontal_distance_squared() + { step_to_delta = target_movement; } } - if step_to_delta.horizontal_distance_sqr() > collided_delta.horizontal_distance_sqr() { + if step_to_delta.horizontal_distance_squared() + > collided_delta.horizontal_distance_squared() + { return step_to_delta.add(collide_bounding_box( &Vec3 { x: 0., @@ -162,7 +166,7 @@ pub fn move_colliding( let collide_result = collide(movement, world, physics); - let move_distance = collide_result.length_sqr(); + let move_distance = collide_result.length_squared(); if move_distance > EPSILON { // TODO: fall damage diff --git a/azalea-physics/src/collision/shape.rs b/azalea-physics/src/collision/shape.rs index fab8d8dc..3f584952 100755 --- a/azalea-physics/src/collision/shape.rs +++ b/azalea-physics/src/collision/shape.rs @@ -423,7 +423,7 @@ impl VoxelShape { return None; } let vector = to - from; - if vector.length_sqr() < EPSILON { + if vector.length_squared() < EPSILON { return None; } let right_after_start = from + &(vector * 0.0001); diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs index 51f45bf0..f64d9eb8 100755 --- a/azalea-protocol/src/connect.rs +++ b/azalea-protocol/src/connect.rs @@ -370,7 +370,7 @@ impl Connection { /// use azalea_protocol::connect::Connection; /// use azalea_protocol::packets::login::{ /// ClientboundLoginPacket, - /// s_key::ServerboundKey + /// ServerboundKey /// }; /// use uuid::Uuid; /// # use azalea_protocol::ServerAddress; diff --git a/azalea-protocol/src/packets/game/c_player_chat.rs b/azalea-protocol/src/packets/game/c_player_chat.rs index fe024ceb..0e9960f2 100644 --- a/azalea-protocol/src/packets/game/c_player_chat.rs +++ b/azalea-protocol/src/packets/game/c_player_chat.rs @@ -8,6 +8,7 @@ use azalea_chat::{ use azalea_core::bitset::BitSet; use azalea_crypto::MessageSignature; use azalea_protocol_macros::ClientboundGamePacket; +use azalea_registry::{ChatType, OptionalRegistry}; use uuid::Uuid; #[derive(Clone, Debug, AzBuf, ClientboundGamePacket, PartialEq)] @@ -51,23 +52,35 @@ pub enum FilterMask { PartiallyFiltered(BitSet), } -#[derive(Copy, Clone, Debug, AzBuf, PartialEq, Eq)] -pub enum ChatType { - Chat = 0, - SayCommand = 1, - MsgCommandIncoming = 2, - MsgCommandOutgoing = 3, - TeamMsgCommandIncoming = 4, - TeamMsgCommandOutgoing = 5, - EmoteCommand = 6, -} - -#[derive(Clone, Debug, AzBuf, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct ChatTypeBound { pub chat_type: ChatType, pub name: FormattedText, pub target_name: Option, } +impl AzaleaRead for ChatTypeBound { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result { + let Some(chat_type) = OptionalRegistry::::azalea_read(buf)?.0 else { + return Err(BufReadError::Custom("ChatType cannot be None".to_owned())); + }; + let name = FormattedText::azalea_read(buf)?; + let target_name = Option::::azalea_read(buf)?; + + Ok(ChatTypeBound { + chat_type, + name, + target_name, + }) + } +} +impl AzaleaWrite for ChatTypeBound { + fn azalea_write(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + OptionalRegistry(Some(self.chat_type)).azalea_write(buf)?; + self.name.azalea_write(buf)?; + self.target_name.azalea_write(buf)?; + Ok(()) + } +} // must be in Client #[derive(Clone, Debug, PartialEq)] @@ -119,29 +132,6 @@ impl ClientboundPlayerChat { } } -impl ChatType { - #[must_use] - pub fn chat_translation_key(&self) -> &'static str { - match self { - ChatType::Chat => "chat.type.text", - ChatType::SayCommand => "chat.type.announcement", - ChatType::MsgCommandIncoming => "commands.message.display.incoming", - ChatType::MsgCommandOutgoing => "commands.message.display.outgoing", - ChatType::TeamMsgCommandIncoming => "chat.type.team.text", - ChatType::TeamMsgCommandOutgoing => "chat.type.team.sent", - ChatType::EmoteCommand => "chat.type.emote", - } - } - - #[must_use] - pub fn narrator_translation_key(&self) -> &'static str { - match self { - ChatType::EmoteCommand => "chat.type.emote", - _ => "chat.type.text.narrate", - } - } -} - impl AzaleaRead for PackedMessageSignature { fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result { let id = u32::azalea_read_var(buf)?; @@ -168,33 +158,3 @@ impl AzaleaWrite for PackedMessageSignature { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_read_player_chat_packet() { - let mut bytes = Cursor::new( - &[ - 55, 186, 28, 76, 92, 167, 177, 75, 188, 158, 200, 179, 191, 227, 16, 171, 145, 0, - 0, 4, 116, 101, 115, 116, 0, 0, 1, 140, 178, 225, 89, 103, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10, 10, 0, 10, 104, 111, 118, 101, 114, 69, 118, 101, 110, 116, 10, 0, - 8, 99, 111, 110, 116, 101, 110, 116, 115, 8, 0, 4, 110, 97, 109, 101, 0, 12, 75, - 97, 115, 117, 109, 105, 77, 97, 114, 105, 115, 97, 11, 0, 2, 105, 100, 0, 0, 0, 4, - 186, 28, 76, 92, 167, 177, 75, 188, 158, 200, 179, 191, 227, 16, 171, 145, 8, 0, 4, - 116, 121, 112, 101, 0, 16, 109, 105, 110, 101, 99, 114, 97, 102, 116, 58, 112, 108, - 97, 121, 101, 114, 0, 8, 0, 6, 97, 99, 116, 105, 111, 110, 0, 11, 115, 104, 111, - 119, 95, 101, 110, 116, 105, 116, 121, 0, 10, 0, 10, 99, 108, 105, 99, 107, 69, - 118, 101, 110, 116, 8, 0, 6, 97, 99, 116, 105, 111, 110, 0, 15, 115, 117, 103, 103, - 101, 115, 116, 95, 99, 111, 109, 109, 97, 110, 100, 8, 0, 5, 118, 97, 108, 117, - 101, 0, 19, 47, 116, 101, 108, 108, 32, 75, 97, 115, 117, 109, 105, 77, 97, 114, - 105, 115, 97, 32, 0, 9, 0, 5, 101, 120, 116, 114, 97, 8, 0, 0, 0, 3, 0, 0, 0, 12, - 75, 97, 115, 117, 109, 105, 77, 97, 114, 105, 115, 97, 0, 0, 8, 0, 9, 105, 110, - 115, 101, 114, 116, 105, 111, 110, 0, 12, 75, 97, 115, 117, 109, 105, 77, 97, 114, - 105, 115, 97, 8, 0, 4, 116, 101, 120, 116, 0, 0, 0, 0, - ][..], - ); - let _packet = ClientboundPlayerChat::azalea_read(&mut bytes).unwrap(); - } -} diff --git a/azalea-protocol/src/packets/game/c_player_info_update.rs b/azalea-protocol/src/packets/game/c_player_info_update.rs index ba43564c..73c463d5 100644 --- a/azalea-protocol/src/packets/game/c_player_info_update.rs +++ b/azalea-protocol/src/packets/game/c_player_info_update.rs @@ -26,6 +26,7 @@ pub struct PlayerInfoEntry { pub game_mode: GameMode, pub display_name: Option, pub list_order: i32, + pub update_hat: bool, pub chat_session: Option, } @@ -56,6 +57,10 @@ pub struct UpdateDisplayNameAction { pub display_name: Option, } #[derive(Clone, Debug, AzBuf)] +pub struct UpdateHatAction { + pub update_hat: bool, +} +#[derive(Clone, Debug, AzBuf)] pub struct UpdateListOrderAction { #[var] pub list_order: i32, @@ -97,6 +102,10 @@ impl AzaleaRead for ClientboundPlayerInfoUpdate { let action = UpdateDisplayNameAction::azalea_read(buf)?; entry.display_name = action.display_name; } + if actions.update_hat { + let action = UpdateHatAction::azalea_read(buf)?; + entry.update_hat = action.update_hat; + } if actions.update_list_order { let action = UpdateListOrderAction::azalea_read(buf)?; entry.list_order = action.list_order; @@ -168,6 +177,7 @@ pub struct ActionEnumSet { pub update_listed: bool, pub update_latency: bool, pub update_display_name: bool, + pub update_hat: bool, pub update_list_order: bool, } @@ -181,7 +191,8 @@ impl AzaleaRead for ActionEnumSet { update_listed: set.index(3), update_latency: set.index(4), update_display_name: set.index(5), - update_list_order: set.index(6), + update_hat: set.index(6), + update_list_order: set.index(7), }) } } @@ -207,9 +218,12 @@ impl AzaleaWrite for ActionEnumSet { if self.update_display_name { set.set(5); } - if self.update_list_order { + if self.update_hat { set.set(6); } + if self.update_list_order { + set.set(7); + } set.azalea_write(buf)?; Ok(()) } @@ -228,6 +242,7 @@ mod tests { update_listed: false, update_latency: true, update_display_name: false, + update_hat: false, update_list_order: true, }; let mut buf = Vec::new(); diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs index 3e333e52..d75e3bad 100755 --- a/azalea-protocol/src/read.rs +++ b/azalea-protocol/src/read.rs @@ -351,7 +351,7 @@ where } if log::log_enabled!(log::Level::Trace) { - const DO_NOT_CUT_OFF_PACKET_LOGS: bool = false; + const DO_NOT_CUT_OFF_PACKET_LOGS: bool = true; let buf_string: String = { if !DO_NOT_CUT_OFF_PACKET_LOGS && buf.len() > 500 { diff --git a/azalea-registry/src/extra.rs b/azalea-registry/src/extra.rs index d5816e3b..4b7e89a4 100644 --- a/azalea-registry/src/extra.rs +++ b/azalea-registry/src/extra.rs @@ -97,3 +97,37 @@ enum JukeboxSong { CreatorMusicBox => "creator_music_box", } } + +registry! { +enum ChatType { + Chat => "chat", + SayCommand => "say_command", + MsgCommandIncoming => "msg_command_incoming", + MsgCommandOutgoing => "msg_command_outgoing", + TeamMsgCommandIncoming => "team_msg_command_incoming", + TeamMsgCommandOutgoing => "team_msg_command_outgoing", + EmoteCommand => "emote_command", +} +} +impl ChatType { + #[must_use] + pub fn chat_translation_key(self) -> &'static str { + match self { + ChatType::Chat => "chat.type.text", + ChatType::SayCommand => "chat.type.announcement", + ChatType::MsgCommandIncoming => "commands.message.display.incoming", + ChatType::MsgCommandOutgoing => "commands.message.display.outgoing", + ChatType::TeamMsgCommandIncoming => "chat.type.team.text", + ChatType::TeamMsgCommandOutgoing => "chat.type.team.sent", + ChatType::EmoteCommand => "chat.type.emote", + } + } + + #[must_use] + pub fn narrator_translation_key(self) -> &'static str { + match self { + ChatType::EmoteCommand => "chat.type.emote", + _ => "chat.type.text.narrate", + } + } +} diff --git a/azalea-registry/src/lib.rs b/azalea-registry/src/lib.rs index 2df30cc7..ffead5b4 100755 --- a/azalea-registry/src/lib.rs +++ b/azalea-registry/src/lib.rs @@ -26,7 +26,7 @@ where /// A registry that might not be present. This is transmitted as a single /// varint in the protocol. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct OptionalRegistry(Option); +pub struct OptionalRegistry(pub Option); impl AzaleaRead for OptionalRegistry { fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result { diff --git a/azalea/src/pathfinder/debug.rs b/azalea/src/pathfinder/debug.rs index a5e51cdf..ca08cbc5 100644 --- a/azalea/src/pathfinder/debug.rs +++ b/azalea/src/pathfinder/debug.rs @@ -60,7 +60,7 @@ pub fn debug_render_path_with_particles( let start_vec3 = start.center(); let end_vec3 = end.center(); - let step_count = (start_vec3.distance_to_sqr(&end_vec3).sqrt() * 4.0) as usize; + let step_count = (start_vec3.distance_squared_to(&end_vec3).sqrt() * 4.0) as usize; let target_block_state = chunks.get_block_state(&movement.target).unwrap_or_default(); let above_target_block_state = chunks diff --git a/azalea/src/pathfinder/goals.rs b/azalea/src/pathfinder/goals.rs index 7e33f7d8..531e4036 100644 --- a/azalea/src/pathfinder/goals.rs +++ b/azalea/src/pathfinder/goals.rs @@ -200,7 +200,7 @@ impl Goal for ReachBlockPosGoal { let max_pick_range = 6; let actual_pick_range = 4.5; - let distance = (self.pos - n).length_sqr(); + let distance = (self.pos - n).length_squared(); if distance > max_pick_range * max_pick_range { return false; } diff --git a/azalea/src/pathfinder/mod.rs b/azalea/src/pathfinder/mod.rs index 611ad5c5..88ae5da0 100644 --- a/azalea/src/pathfinder/mod.rs +++ b/azalea/src/pathfinder/mod.rs @@ -453,7 +453,7 @@ pub fn timeout_movement( // don't timeout if we're mining if let Some(mining) = mining { // also make sure we're close enough to the block that's being mined - if mining.pos.distance_to_sqr(&BlockPos::from(position)) < 6_i32.pow(2) { + if mining.pos.distance_squared_to(&BlockPos::from(position)) < 6_i32.pow(2) { // also reset the last_node_reached_at so we don't timeout after we finish // mining executing_path.last_node_reached_at = Instant::now(); diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs index bb931caf..4f0d522a 100644 --- a/azalea/src/pathfinder/moves/basic.rs +++ b/azalea/src/pathfinder/moves/basic.rs @@ -228,9 +228,9 @@ fn execute_descend_move(mut ctx: ExecuteCtx) { let start_center = start.center(); let center = target.center(); - let horizontal_distance_from_target = (center - position).horizontal_distance_sqr().sqrt(); + let horizontal_distance_from_target = (center - position).horizontal_distance_squared().sqrt(); let horizontal_distance_from_start = - (start.center() - position).horizontal_distance_sqr().sqrt(); + (start.center() - position).horizontal_distance_squared().sqrt(); let dest_ahead = Vec3::new( start_center.x + (center.x - start_center.x) * 1.5, @@ -402,7 +402,7 @@ fn execute_downward_move(mut ctx: ExecuteCtx) { let target_center = target.center(); let horizontal_distance_from_target = - (target_center - position).horizontal_distance_sqr().sqrt(); + (target_center - position).horizontal_distance_squared().sqrt(); if horizontal_distance_from_target > 0.25 { ctx.look_at(target_center); diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs index 28974132..1a435b5f 100644 --- a/azalea/src/pathfinder/moves/mod.rs +++ b/azalea/src/pathfinder/moves/mod.rs @@ -157,7 +157,7 @@ impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> { /// of the current node first. pub fn mine_while_at_start(&mut self, block: BlockPos) -> bool { let horizontal_distance_from_start = (self.start.center() - self.position) - .horizontal_distance_sqr() + .horizontal_distance_squared() .sqrt(); let at_start_position = BlockPos::from(self.position) == self.start && horizontal_distance_from_start < 0.25;