steel_core/behavior/blocks/vegetation/
weeping_vines_plant_block.rs1use rand::Rng;
2use std::sync::Arc;
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::{item_stack::ItemStack, vanilla_blocks, vanilla_items};
6use steel_utils::{BlockPos, BlockStateId, Direction};
7
8use crate::behavior::blocks::vegetation::bonemealable::BonemealAction;
9use crate::behavior::blocks::vegetation::growing_plant_body_block::GrowingPlantBodyBlock;
10use crate::behavior::context::BlockPlaceContext;
11use crate::behavior::{block::BlockBehavior, blocks::vegetation::bonemealable::Bonemealable};
12use crate::world::{LevelReader, ScheduledTickAccess, World};
13
14use super::BlockRef;
15
16#[block_behavior]
18pub struct WeepingVinesPlantBlock {
19 block: BlockRef,
20}
21
22impl WeepingVinesPlantBlock {
23 #[must_use]
25 pub const fn new(block: BlockRef) -> Self {
26 Self { block }
27 }
28
29 fn can_grow_into(state: BlockStateId) -> bool {
30 state.is_air()
31 }
32
33 const fn growing_plant_body_block(&self) -> GrowingPlantBodyBlock {
34 GrowingPlantBodyBlock::new(
35 self.block,
36 Direction::Down,
37 false,
38 &vanilla_blocks::WEEPING_VINES,
39 Self::can_grow_into,
40 )
41 }
42}
43
44impl BlockBehavior for WeepingVinesPlantBlock {
45 fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
46 self.growing_plant_body_block()
47 .can_survive(state, world, pos)
48 }
49
50 fn random_tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
51 self.growing_plant_body_block()
52 .random_tick(state, world, pos);
53 }
54
55 fn update_shape(
56 &self,
57 state: BlockStateId,
58 world: &dyn ScheduledTickAccess,
59 pos: BlockPos,
60 direction: Direction,
61 neighbor_pos: BlockPos,
62 neighbor_state: BlockStateId,
63 ) -> BlockStateId {
64 self.growing_plant_body_block().update_shape(
65 state,
66 world,
67 pos,
68 direction,
69 neighbor_pos,
70 neighbor_state,
71 )
72 }
73 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
74 self.growing_plant_body_block().tick(state, world, pos);
75 }
76
77 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
78 self.growing_plant_body_block()
79 .get_state_for_placement(context)
80 }
81
82 fn get_clone_item_stack(
83 &self,
84 _block: BlockRef,
85 _state: BlockStateId,
86 _include_data: bool,
87 ) -> Option<ItemStack> {
88 Some(ItemStack::new(&vanilla_items::WEEPING_VINES))
89 }
90
91 fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
92 Some(self)
93 }
94}
95impl Bonemealable for WeepingVinesPlantBlock {
96 fn is_valid_bonemeal_target(
97 &self,
98 state: BlockStateId,
99 world: &dyn LevelReader,
100 pos: BlockPos,
101 ) -> bool {
102 self.growing_plant_body_block()
103 .is_valid_bonemeal_target(state, world, pos)
104 }
105
106 fn perform_bonemeal(
107 &self,
108 state: BlockStateId,
109 world: &Arc<World>,
110 rng: &mut dyn Rng,
111 pos: BlockPos,
112 ) {
113 self.growing_plant_body_block()
114 .perform_bonemeal(state, world, rng, pos);
115 }
116
117 fn bonemeal_action_type(&self) -> BonemealAction {
118 BonemealAction::Grower
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use steel_registry::init_vanilla_registry;
125
126 use super::*;
127 use crate::test_support::TestLevel;
128
129 #[test]
130 fn bonemeal_target_follows_connected_head() {
131 init_vanilla_registry();
132
133 let behavior = WeepingVinesPlantBlock::new(&vanilla_blocks::WEEPING_VINES_PLANT);
134 let state = vanilla_blocks::WEEPING_VINES_PLANT.default_state();
135 let open_level = TestLevel::default().with_block(
136 BlockPos::ZERO.below(),
137 vanilla_blocks::WEEPING_VINES.default_state(),
138 );
139 assert!(behavior.is_valid_bonemeal_target(state, &open_level, BlockPos::ZERO));
140
141 let blocked_level = open_level.with_block(
142 BlockPos::ZERO.below().below(),
143 vanilla_blocks::NETHERRACK.default_state(),
144 );
145 assert!(!behavior.is_valid_bonemeal_target(state, &blocked_level, BlockPos::ZERO));
146 }
147}