Skip to main content

steel_core/behavior/blocks/vegetation/
weeping_vines_plant_block.rs

1use 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/// Vanilla `WeepingVinesPlantBlock` (body) survival.
17#[block_behavior]
18pub struct WeepingVinesPlantBlock {
19    block: BlockRef,
20}
21
22impl WeepingVinesPlantBlock {
23    /// Creates a new weeping vines plant (body) block behavior.
24    #[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}