Skip to main content

steel_core/behavior/blocks/vegetation/
sea_pickle_block.rs

1use steel_macros::block_behavior;
2use steel_registry::blocks::block_state_ext::BlockStateExt;
3use steel_registry::blocks::properties::Direction;
4use steel_registry::vanilla_blocks;
5use steel_utils::{BlockPos, BlockStateId};
6
7use crate::behavior::block::{BlockBehavior, schedule_water_tick_if_waterlogged};
8use crate::behavior::context::BlockPlaceContext;
9use crate::behavior::{BLOCK_BEHAVIORS, BlockCollisionContext};
10use crate::world::{LevelReader, ScheduledTickAccess};
11
12use super::{BlockRef, default_surviving_state};
13
14/// Vanilla `SeaPickleBlock` survival.
15// TODO: Implement full vanilla behavior beyond can_survive.
16#[block_behavior]
17pub struct SeaPickleBlock {
18    block: BlockRef,
19}
20
21impl SeaPickleBlock {
22    /// Creates a new sea pickle block behavior.
23    #[must_use]
24    pub const fn new(block: BlockRef) -> Self {
25        Self { block }
26    }
27
28    fn may_place_on(world: &dyn LevelReader, state: BlockStateId, pos: BlockPos) -> bool {
29        BLOCK_BEHAVIORS
30            .get_behavior(state.get_block())
31            .get_collision_boxes(state, world, pos, BlockCollisionContext::empty())
32            .iter()
33            .any(|aabb| !aabb.is_empty() && aabb.max_y() >= 1.0)
34            || world.is_face_sturdy(state, pos, Direction::Up)
35    }
36}
37
38impl BlockBehavior for SeaPickleBlock {
39    fn update_shape(
40        &self,
41        state: BlockStateId,
42        world: &dyn ScheduledTickAccess,
43        pos: BlockPos,
44        _direction: Direction,
45        _neighbor_pos: BlockPos,
46        _neighbor_state: BlockStateId,
47    ) -> BlockStateId {
48        if !self.can_survive(state, world, pos) {
49            return vanilla_blocks::AIR.default_state();
50        }
51
52        schedule_water_tick_if_waterlogged(state, world, pos);
53        state
54    }
55
56    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
57        let below_pos = pos.below();
58        Self::may_place_on(world, world.get_block_state(below_pos), below_pos)
59    }
60
61    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
62        default_surviving_state(self.block, self, context)
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use steel_registry::blocks::properties::BlockStateProperties;
69    use steel_registry::{init_vanilla_registry, vanilla_fluids};
70
71    use super::*;
72    use crate::behavior::init_behaviors;
73    use crate::test_support::TestLevel;
74
75    #[test]
76    fn sea_pickle_checks_survival_before_scheduling_water() {
77        init_vanilla_registry();
78        init_behaviors();
79        let behavior = SeaPickleBlock::new(&vanilla_blocks::SEA_PICKLE);
80        let state = vanilla_blocks::SEA_PICKLE
81            .default_state()
82            .set_value(&BlockStateProperties::WATERLOGGED, true);
83        let pos = BlockPos::new(0, 64, 0);
84        let unsupported = TestLevel::default();
85
86        assert!(
87            behavior
88                .update_shape(
89                    state,
90                    &unsupported,
91                    pos,
92                    Direction::North,
93                    pos.north(),
94                    vanilla_blocks::AIR.default_state(),
95                )
96                .is_air()
97        );
98        assert!(unsupported.scheduled_fluid_ticks.borrow().is_empty());
99
100        let supported =
101            TestLevel::default().with_block(pos.below(), vanilla_blocks::STONE.default_state());
102        assert_eq!(
103            behavior.update_shape(
104                state,
105                &supported,
106                pos,
107                Direction::North,
108                pos.north(),
109                vanilla_blocks::AIR.default_state(),
110            ),
111            state
112        );
113        assert_eq!(
114            supported
115                .scheduled_fluid_ticks
116                .borrow()
117                .iter()
118                .map(|tick| (tick.fluid, tick.delay))
119                .collect::<Vec<_>>(),
120            vec![(&vanilla_fluids::WATER, 5)]
121        );
122    }
123}