steel_core/entity/ai/walk/
path_evaluator.rs1use 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}