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