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    base: GrowingPlantBodyBlock,
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 {
27            base: GrowingPlantBodyBlock::new(
28                block,
29                Direction::Down,
30                false,
31                &vanilla_blocks::WEEPING_VINES,
32                Self::can_grow_into,
33            ),
34        }
35    }
36
37    fn can_grow_into(state: BlockStateId) -> bool {
38        state.is_air()
39    }
40}
41
42impl BlockBehavior for WeepingVinesPlantBlock {
43    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
44        self.base.can_survive(state, world, pos)
45    }
46
47    fn random_tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
48        self.base.random_tick(state, world, pos);
49    }
50
51    fn update_shape(
52        &self,
53        state: BlockStateId,
54        world: &dyn ScheduledTickAccess,
55        pos: BlockPos,
56        direction: Direction,
57        neighbor_pos: BlockPos,
58        neighbor_state: BlockStateId,
59    ) -> BlockStateId {
60        self.base
61            .update_shape(state, world, pos, direction, neighbor_pos, neighbor_state)
62    }
63    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
64        self.base.tick(state, world, pos);
65    }
66
67    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
68        self.base.get_state_for_placement(context)
69    }
70
71    fn get_clone_item_stack(
72        &self,
73        _block: BlockRef,
74        _state: BlockStateId,
75        _include_data: bool,
76    ) -> Option<ItemStack> {
77        Some(ItemStack::new(&vanilla_items::WEEPING_VINES))
78    }
79
80    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
81        Some(self)
82    }
83}
84impl Bonemealable for WeepingVinesPlantBlock {
85    fn is_valid_bonemeal_target(
86        &self,
87        state: BlockStateId,
88        world: &dyn LevelReader,
89        pos: BlockPos,
90    ) -> bool {
91        self.base.is_valid_bonemeal_target(state, world, pos)
92    }
93
94    fn perform_bonemeal(
95        &self,
96        state: BlockStateId,
97        world: &Arc<World>,
98        rng: &mut dyn Rng,
99        pos: BlockPos,
100    ) {
101        self.base.perform_bonemeal(state, world, rng, pos);
102    }
103
104    fn bonemeal_action_type(&self) -> BonemealAction {
105        BonemealAction::Grower
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use steel_registry::init_vanilla_registry;
112
113    use super::*;
114    use crate::test_support::TestLevel;
115
116    #[test]
117    fn bonemeal_target_follows_connected_head() {
118        init_vanilla_registry();
119
120        let behavior = WeepingVinesPlantBlock::new(&vanilla_blocks::WEEPING_VINES_PLANT);
121        let state = vanilla_blocks::WEEPING_VINES_PLANT.default_state();
122        let open_level = TestLevel::default().with_block(
123            BlockPos::ZERO.below(),
124            vanilla_blocks::WEEPING_VINES.default_state(),
125        );
126        assert!(behavior.is_valid_bonemeal_target(state, &open_level, BlockPos::ZERO));
127
128        let blocked_level = open_level.with_block(
129            BlockPos::ZERO.below().below(),
130            vanilla_blocks::NETHERRACK.default_state(),
131        );
132        assert!(!behavior.is_valid_bonemeal_target(state, &blocked_level, BlockPos::ZERO));
133    }
134}