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