steel_core/fluid/
collision.rs1use std::sync::Arc;
6
7use crate::behavior::BlockStateBehaviorExt;
8use crate::behavior::{BLOCK_BEHAVIORS, FLUID_BEHAVIORS};
9use crate::physics::shapes::merged_offset_face_occludes;
10use crate::world::World;
11use steel_registry::blocks::BlockRef;
12use steel_registry::blocks::block_state_ext::BlockStateExt;
13use steel_registry::blocks::properties::Direction;
14use steel_registry::fluid::FluidRef;
15use steel_registry::vanilla_block_tags::BlockTag;
16use steel_registry::vanilla_blocks;
17use steel_utils::{BlockPos, BlockStateId};
18
19#[must_use]
23pub fn can_pass_through_wall(
24 world: &Arc<World>,
25 from: BlockPos,
26 from_state: BlockStateId,
27 to: BlockPos,
28 to_state: BlockStateId,
29 direction: Direction,
30) -> bool {
31 if !world.is_in_valid_bounds(to) {
32 return false;
33 }
34
35 let from_shape = from_state.get_collision_shape_at(from);
36 let to_shape = to_state.get_collision_shape_at(to);
37
38 !merged_offset_face_occludes(from_shape, to_shape, direction)
39}
40
41#[must_use]
47pub fn can_hold_any_fluid_state(state: BlockStateId) -> bool {
48 let block = state.get_block();
49
50 if state.is_liquid_container() {
52 return true;
53 }
54
55 if state.blocks_motion() {
57 return false;
58 }
59
60 !is_fluid_excluded_block(block)
62}
63
64fn is_fluid_excluded_block(block: BlockRef) -> bool {
66 block == &vanilla_blocks::LADDER
67 || block == &vanilla_blocks::SUGAR_CANE
68 || block == &vanilla_blocks::BUBBLE_COLUMN
69 || block == &vanilla_blocks::NETHER_PORTAL
70 || block == &vanilla_blocks::END_PORTAL
71 || block == &vanilla_blocks::END_GATEWAY
72 || block == &vanilla_blocks::STRUCTURE_VOID
73 || block.has_tag(&BlockTag::SIGNS)
74 || block.has_tag(&BlockTag::DOORS)
75}
76
77#[must_use]
82pub fn can_hold_specific_fluid(state: BlockStateId, fluid: FluidRef) -> bool {
83 if state.is_liquid_container() {
84 let behavior = BLOCK_BEHAVIORS.get_behavior(state.get_block());
85 behavior.can_place_liquid(state, fluid)
86 } else {
87 true
88 }
89}
90
91#[must_use]
95pub fn can_hold_fluid(state: BlockStateId, fluid: FluidRef) -> bool {
96 can_hold_any_fluid_state(state) && can_hold_specific_fluid(state, fluid)
97}
98
99#[must_use]
103pub fn can_pass_horizontally_internal(state: BlockStateId, target_fluid_id: FluidRef) -> bool {
104 let fluid_state = state.get_fluid_state();
106 if FLUID_BEHAVIORS
107 .get_behavior(target_fluid_id)
108 .is_same(fluid_state.fluid_id)
109 && fluid_state.is_source()
110 {
111 return false;
112 }
113
114 can_hold_any_fluid_state(state)
116}
117
118#[cfg(test)]
119mod tests {
120 use crate::behavior::init_behaviors;
121 use steel_registry::blocks::properties::{BlockStateProperties, SlabType};
122 use steel_registry::{init_vanilla_registry, vanilla_blocks, vanilla_fluids};
123
124 use super::*;
125
126 #[test]
127 fn waterlogged_source_blocks_block_same_fluid_flowing_variant_spread() {
128 init_vanilla_registry();
129 init_behaviors();
130
131 let waterlogged = vanilla_blocks::OAK_LEAVES
132 .default_state()
133 .set_value(&BlockStateProperties::WATERLOGGED, true);
134
135 assert!(!can_pass_horizontally_internal(
136 waterlogged,
137 &vanilla_fluids::FLOWING_WATER
138 ));
139 assert!(!can_pass_horizontally_internal(
140 waterlogged,
141 &vanilla_fluids::WATER
142 ));
143 }
144
145 #[test]
146 fn dry_waterloggable_blocks_reject_flowing_water() {
147 init_vanilla_registry();
148 init_behaviors();
149
150 let dry = vanilla_blocks::OAK_SLAB
151 .default_state()
152 .set_value(&BlockStateProperties::SLAB_TYPE, SlabType::Bottom)
153 .set_value(&BlockStateProperties::WATERLOGGED, false);
154
155 assert!(can_hold_specific_fluid(dry, &vanilla_fluids::WATER));
156 assert!(!can_hold_specific_fluid(
157 dry,
158 &vanilla_fluids::FLOWING_WATER
159 ));
160 }
161
162 #[test]
163 fn kelp_is_liquid_container_without_waterlogged_property() {
164 init_vanilla_registry();
165 init_behaviors();
166
167 let kelp = vanilla_blocks::KELP.default_state();
168
169 assert!(
170 kelp.try_get_value(&BlockStateProperties::WATERLOGGED)
171 .is_none()
172 );
173 assert!(kelp.is_liquid_container());
174 assert!(can_hold_any_fluid_state(kelp));
175 assert!(!can_hold_specific_fluid(kelp, &vanilla_fluids::WATER));
176 assert!(!can_hold_specific_fluid(kelp, &vanilla_fluids::LAVA));
177 }
178}