Skip to main content

steel_core/entity/ai/walk/
path_evaluator.rs

1use super::*;
2
3pub(super) const fn does_block_have_partial_collision(path_type: PathType) -> bool {
4    matches!(
5        path_type,
6        PathType::Fence | PathType::DoorWoodClosed | PathType::DoorIronClosed
7    )
8}
9
10pub struct WalkPathEvaluator;
11
12impl WalkPathEvaluator {
13    #[must_use]
14    pub fn path_type(context: &mut PathfindingContext<'_>, x: i32, y: i32, z: i32) -> PathType {
15        Self::path_type_static(context, BlockPos::new(x, y, z))
16    }
17
18    #[must_use]
19    pub fn path_type_static(context: &mut PathfindingContext<'_>, pos: BlockPos) -> PathType {
20        let x = pos.x();
21        let y = pos.y();
22        let z = pos.z();
23        let block_path_type = context.get_path_type_from_state(x, y, z);
24        if block_path_type != PathType::Open || y < context.level().min_y() + 1 {
25            return block_path_type;
26        }
27
28        match context.get_path_type_from_state(x, y - 1, z) {
29            PathType::Open | PathType::Water | PathType::Lava | PathType::Walkable => {
30                PathType::Open
31            }
32            PathType::Fire => PathType::Fire,
33            PathType::Damaging => PathType::Damaging,
34            PathType::StickyHoney => PathType::StickyHoney,
35            PathType::PowderSnow => PathType::OnTopOfPowderSnow,
36            PathType::DamageCautious => PathType::DamageCautious,
37            PathType::Trapdoor => PathType::OnTopOfTrapdoor,
38            _ => Self::check_neighbour_blocks(context, x, y, z, PathType::Walkable),
39        }
40    }
41
42    #[must_use]
43    pub fn check_neighbour_blocks(
44        context: &mut PathfindingContext<'_>,
45        x: i32,
46        y: i32,
47        z: i32,
48        block_path_type: PathType,
49    ) -> PathType {
50        for dx in -1..=1 {
51            for dy in -1..=1 {
52                for dz in -1..=1 {
53                    if dx == 0 && dz == 0 {
54                        continue;
55                    }
56
57                    match context.get_path_type_from_state(x + dx, y + dy, z + dz) {
58                        PathType::Damaging => return PathType::DamagingInNeighbor,
59                        PathType::Fire | PathType::Lava => return PathType::FireInNeighbor,
60                        PathType::Water => return PathType::WaterBorder,
61                        PathType::DamageCautious => return PathType::DamageCautious,
62                        _ => {}
63                    }
64                }
65            }
66        }
67
68        block_path_type
69    }
70
71    #[must_use]
72    pub fn path_type_from_state(level: &dyn LevelReader, pos: BlockPos) -> PathType {
73        let block_state = level.get_block_state(pos);
74        let block = block_state.get_block();
75        if block_state.is_air() {
76            return PathType::Open;
77        }
78
79        if block.has_tag(&BlockTag::TRAPDOORS)
80            || block == &vanilla_blocks::LILY_PAD
81            || block == &vanilla_blocks::BIG_DRIPLEAF
82        {
83            return PathType::Trapdoor;
84        }
85
86        if block == &vanilla_blocks::POWDER_SNOW {
87            return PathType::PowderSnow;
88        }
89
90        if block == &vanilla_blocks::CACTUS || block == &vanilla_blocks::SWEET_BERRY_BUSH {
91            return PathType::Damaging;
92        }
93
94        if block == &vanilla_blocks::HONEY_BLOCK {
95            return PathType::StickyHoney;
96        }
97
98        if block == &vanilla_blocks::COCOA {
99            return PathType::Cocoa;
100        }
101
102        if block == &vanilla_blocks::WITHER_ROSE || block.has_tag(&BlockTag::SPELEOTHEMS) {
103            return PathType::DamageCautious;
104        }
105
106        let fluid_state = block_state.get_fluid_state();
107        if fluid_state.is_lava() {
108            return PathType::Lava;
109        }
110
111        if Self::is_burning_block(block_state) {
112            return PathType::Fire;
113        }
114
115        if block.has_tag(&BlockTag::DOORS) {
116            return if block_state
117                .try_get_value(&BlockStateProperties::OPEN)
118                .unwrap_or(false)
119            {
120                PathType::DoorOpen
121            } else if block.has_tag(&BlockTag::MOB_INTERACTABLE_DOORS) {
122                PathType::DoorWoodClosed
123            } else {
124                PathType::DoorIronClosed
125            };
126        }
127
128        if block.has_tag(&BlockTag::RAILS) {
129            return PathType::Rail;
130        }
131
132        if block.has_tag(&BlockTag::LEAVES) {
133            return PathType::Leaves;
134        }
135
136        if block.has_tag(&BlockTag::FENCES)
137            || block.has_tag(&BlockTag::WALLS)
138            || block.has_tag(&BlockTag::FENCE_GATES)
139                && !block_state
140                    .try_get_value(&BlockStateProperties::OPEN)
141                    .unwrap_or(false)
142        {
143            return PathType::Fence;
144        }
145
146        if !block_state.is_pathfindable(PathComputationType::Land) {
147            return PathType::Blocked;
148        }
149
150        if fluid_state.is_water() {
151            PathType::Water
152        } else {
153            PathType::Open
154        }
155    }
156
157    #[must_use]
158    pub fn is_burning_block(block_state: steel_utils::BlockStateId) -> bool {
159        let block = block_state.get_block();
160        block.has_tag(&BlockTag::FIRE)
161            || block == &vanilla_blocks::LAVA
162            || block == &vanilla_blocks::MAGMA_BLOCK
163            || block == &vanilla_blocks::LAVA_CAULDRON
164            || block.has_tag(&BlockTag::CAMPFIRES)
165                && block_state
166                    .try_get_value(&BlockStateProperties::LIT)
167                    .unwrap_or(false)
168    }
169}