steel_core/behavior/blocks/vegetation/
sea_pickle_block.rs1use 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#[block_behavior]
17pub struct SeaPickleBlock {
18 block: BlockRef,
19}
20
21impl SeaPickleBlock {
22 #[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}