1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 06:16:04 +00:00

fix some edge cases when pathfinding on slabs and stairs

This commit is contained in:
mat 2025-05-07 07:09:48 +09:00
parent f7c9419045
commit 7b442368da
3 changed files with 104 additions and 2 deletions

View file

@ -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());

View file

@ -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> {

View file

@ -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 {