aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--azalea/src/pathfinder/moves/basic.rs63
-rw-r--r--azalea/src/pathfinder/moves/mod.rs8
-rw-r--r--azalea/src/pathfinder/world.rs35
3 files changed, 104 insertions, 2 deletions
diff --git a/azalea/src/pathfinder/moves/basic.rs b/azalea/src/pathfinder/moves/basic.rs
index d23bb894..c7fccf26 100644
--- a/azalea/src/pathfinder/moves/basic.rs
+++ b/azalea/src/pathfinder/moves/basic.rs
@@ -1,5 +1,6 @@
use std::f32::consts::SQRT_2;
+use azalea_block::{BlockState, properties};
use azalea_client::{SprintDirection, WalkDirection};
use azalea_core::{
direction::CardinalDirection,
@@ -58,7 +59,34 @@ fn execute_forward_move(mut ctx: ExecuteCtx) {
}
fn ascend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
+ // the block we're standing on must be solid (so we don't try to ascend from a
+ // bottom slab to a normal block in a way that's not possible)
+
+ let is_unusual_shape = !ctx.world.is_block_solid(pos.down(1));
+ let mut stair_facing = None;
+
+ if is_unusual_shape {
+ // this is potentially expensive but it's rare enough that it shouldn't matter
+ // much
+ let block_below = ctx.world.get_block_state(pos.down(1));
+
+ let Some(found_stair_facing) = validate_stair_and_get_facing(block_below) else {
+ // return if it's not a stair or it's not facing the right way (like, if it's
+ // upside down or something)
+ return;
+ };
+
+ stair_facing = Some(found_stair_facing);
+ }
+
for dir in CardinalDirection::iter() {
+ if let Some(stair_facing) = stair_facing {
+ let expected_stair_facing = cardinal_direction_to_facing_property(dir);
+ if stair_facing != expected_stair_facing {
+ continue;
+ }
+ }
+
let offset = RelBlockPos::new(dir.x(), 1, dir.z());
let break_cost_1 = ctx
@@ -134,6 +162,24 @@ fn execute_ascend_move(mut ctx: ExecuteCtx) {
return;
}
+ // if the target block is a stair that's facing in the direction we're going, we
+ // shouldn't jump
+ let block_below_target = ctx.get_block_state(target.down(1));
+ if let Some(stair_facing) = validate_stair_and_get_facing(block_below_target) {
+ let expected_stair_facing = match (x_axis, z_axis) {
+ (0, 1) => Some(properties::FacingCardinal::North),
+ (1, 0) => Some(properties::FacingCardinal::East),
+ (0, -1) => Some(properties::FacingCardinal::South),
+ (-1, 0) => Some(properties::FacingCardinal::West),
+ _ => None,
+ };
+ if let Some(expected_stair_facing) = expected_stair_facing {
+ if stair_facing == expected_stair_facing {
+ return;
+ }
+ }
+ }
+
if BlockPos::from(position) == start {
// only jump if the target is more than 0.5 blocks above us
if target.y as f64 - position.y > 0.5 {
@@ -150,6 +196,23 @@ pub fn ascend_is_reached(
BlockPos::from(position) == target || BlockPos::from(position) == target.down(1)
}
+fn validate_stair_and_get_facing(block_state: BlockState) -> Option<properties::FacingCardinal> {
+ let top_bottom = block_state.property::<properties::TopBottom>();
+ if top_bottom != Some(properties::TopBottom::Bottom) {
+ return None;
+ }
+
+ block_state.property::<properties::FacingCardinal>()
+}
+fn cardinal_direction_to_facing_property(dir: CardinalDirection) -> properties::FacingCardinal {
+ match dir {
+ CardinalDirection::North => properties::FacingCardinal::North,
+ CardinalDirection::East => properties::FacingCardinal::East,
+ CardinalDirection::South => properties::FacingCardinal::South,
+ CardinalDirection::West => properties::FacingCardinal::West,
+ }
+}
+
fn descend_move(ctx: &mut PathfinderCtx, pos: RelBlockPos) {
for dir in CardinalDirection::iter() {
let dir_delta = RelBlockPos::new(dir.x(), 0, dir.z());
diff --git a/azalea/src/pathfinder/moves/mod.rs b/azalea/src/pathfinder/moves/mod.rs
index f79f7249..248f0a5c 100644
--- a/azalea/src/pathfinder/moves/mod.rs
+++ b/azalea/src/pathfinder/moves/mod.rs
@@ -3,6 +3,7 @@ pub mod parkour;
use std::{fmt::Debug, sync::Arc};
+use azalea_block::BlockState;
use azalea_client::{
SprintDirection, StartSprintEvent, StartWalkEvent, WalkDirection,
inventory::SetSelectedHotbarSlotEvent, mining::StartMiningBlockEvent,
@@ -175,6 +176,13 @@ impl ExecuteCtx<'_, '_, '_, '_, '_, '_, '_> {
false
}
}
+
+ pub fn get_block_state(&self, block: BlockPos) -> BlockState {
+ self.instance
+ .read()
+ .get_block_state(&block)
+ .unwrap_or_default()
+ }
}
pub struct IsReachedCtx<'a> {
diff --git a/azalea/src/pathfinder/world.rs b/azalea/src/pathfinder/world.rs
index 45d05810..3b1b36b9 100644
--- a/azalea/src/pathfinder/world.rs
+++ b/azalea/src/pathfinder/world.rs
@@ -3,7 +3,7 @@ use std::{
sync::Arc,
};
-use azalea_block::BlockState;
+use azalea_block::{BlockState, properties};
use azalea_core::{
bitset::FixedBitSet,
position::{BlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos},
@@ -241,6 +241,21 @@ impl CachedWorld {
passable
}
+ /// Get the block state at the given position. This is relatively slow, so
+ /// you should avoid it whenever possible.
+ pub fn get_block_state(&self, pos: RelBlockPos) -> BlockState {
+ self.get_block_state_at_pos(pos.apply(self.origin))
+ }
+
+ fn get_block_state_at_pos(&self, pos: BlockPos) -> BlockState {
+ let (section_pos, section_block_pos) =
+ (ChunkSectionPos::from(pos), ChunkSectionBlockPos::from(pos));
+ let index = u16::from(section_block_pos) as usize;
+
+ self.with_section(section_pos, |section| section.get_at_index(index))
+ .unwrap_or_default()
+ }
+
pub fn is_block_solid(&self, pos: RelBlockPos) -> bool {
self.is_block_pos_solid(pos.apply(self.origin))
}
@@ -487,6 +502,10 @@ impl CachedWorld {
}
distance
}
+
+ pub fn origin(&self) -> BlockPos {
+ self.origin
+ }
}
/// whether this block is passable
@@ -537,7 +556,19 @@ pub fn is_block_state_solid(block: BlockState) -> bool {
// fast path
return false;
}
- block.is_collision_shape_full()
+ if block.is_collision_shape_full() {
+ return true;
+ }
+
+ if matches!(
+ block.property::<properties::Type>(),
+ Some(properties::Type::Top | properties::Type::Double)
+ ) {
+ // top slabs
+ return true;
+ }
+
+ false
}
pub fn is_block_state_standable(block: BlockState) -> bool {