Skip to main content

steel_core/behavior/blocks/building/
barrier_block.rs

1use steel_macros::block_behavior;
2use steel_registry::blocks::BlockRef;
3use steel_registry::blocks::block_state_ext::BlockStateExt as _;
4use steel_registry::blocks::properties::{BlockStateProperties, Direction};
5use steel_registry::fluid::FluidRef;
6use steel_registry::vanilla_fluids;
7use steel_utils::types::GameType;
8use steel_utils::{BlockPos, BlockStateId};
9
10use crate::behavior::{BlockBehavior, BlockPlaceContext};
11use crate::player::Player;
12use crate::world::ScheduledTickAccess;
13
14/// Vanilla `BarrierBlock` liquid-container behavior.
15#[block_behavior]
16pub struct BarrierBlock {
17    block: BlockRef,
18}
19
20impl BarrierBlock {
21    /// Creates a new barrier block behavior.
22    #[must_use]
23    pub const fn new(block: BlockRef) -> Self {
24        Self { block }
25    }
26}
27
28impl BlockBehavior for BarrierBlock {
29    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
30        Some(self.block.default_state().set_value(
31            &BlockStateProperties::WATERLOGGED,
32            context.is_water_source(),
33        ))
34    }
35
36    fn update_shape(
37        &self,
38        state: BlockStateId,
39        world: &dyn ScheduledTickAccess,
40        pos: BlockPos,
41        _direction: Direction,
42        _neighbor_pos: BlockPos,
43        _neighbor_state: BlockStateId,
44    ) -> BlockStateId {
45        if state.get_value(&BlockStateProperties::WATERLOGGED) {
46            let delay = world.fluid_tick_delay(&vanilla_fluids::WATER);
47            let _ = world.schedule_fluid_tick_default(pos, &vanilla_fluids::WATER, delay);
48        }
49
50        state
51    }
52
53    fn can_place_liquid(&self, _state: BlockStateId, _fluid: FluidRef) -> bool {
54        false
55    }
56
57    fn can_place_liquid_with_player(
58        &self,
59        state: BlockStateId,
60        fluid: FluidRef,
61        player: Option<&Player>,
62    ) -> bool {
63        player.is_some_and(|player| player.game_mode() == GameType::Creative)
64            && state
65                .try_get_value(&BlockStateProperties::WATERLOGGED)
66                .is_some()
67            && fluid == &vanilla_fluids::WATER
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::behavior::{BLOCK_BEHAVIORS, init_behaviors};
75    use crate::test_support::TestLevel;
76    use steel_registry::{init_vanilla_registry, vanilla_blocks};
77
78    #[test]
79    fn registered_barrier_rejects_no_user_liquid_placement() {
80        init_vanilla_registry();
81        init_behaviors();
82        let behavior = BLOCK_BEHAVIORS.get_behavior(&vanilla_blocks::BARRIER);
83        let dry_barrier = vanilla_blocks::BARRIER
84            .default_state()
85            .set_value(&BlockStateProperties::WATERLOGGED, false);
86
87        assert!(behavior.is_liquid_container(dry_barrier));
88        assert!(!behavior.can_place_liquid(dry_barrier, &vanilla_fluids::WATER));
89    }
90
91    #[test]
92    fn waterlogged_barrier_update_shape_schedules_water_tick() {
93        init_vanilla_registry();
94
95        let behavior = BarrierBlock::new(&vanilla_blocks::BARRIER);
96        let state = vanilla_blocks::BARRIER
97            .default_state()
98            .set_value(&BlockStateProperties::WATERLOGGED, true);
99        let level = TestLevel::default();
100
101        let updated = behavior.update_shape(
102            state,
103            &level,
104            BlockPos::ZERO,
105            Direction::North,
106            Direction::North.relative(BlockPos::ZERO),
107            vanilla_blocks::AIR.default_state(),
108        );
109
110        assert_eq!(updated, state);
111        assert!(level.scheduled_water_tick());
112    }
113}