Skip to main content

steel_core/behavior/blocks/vegetation/
twisting_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, Bonemealable};
9use crate::behavior::context::BlockPlaceContext;
10use crate::behavior::{
11    block::BlockBehavior, blocks::vegetation::growing_plant_body_block::GrowingPlantBodyBlock,
12};
13use crate::world::{LevelReader, ScheduledTickAccess, World};
14
15use super::BlockRef;
16
17/// Vanilla `TwistingVinesPlantBlock` (body) survival.
18#[block_behavior]
19pub struct TwistingVinesPlantBlock {
20    base: GrowingPlantBodyBlock,
21}
22
23impl TwistingVinesPlantBlock {
24    /// Creates a new twisting 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::Up,
31                false,
32                &vanilla_blocks::TWISTING_VINES,
33                Self::can_grow_into,
34            ),
35        }
36    }
37
38    fn can_grow_into(state: BlockStateId) -> bool {
39        state.is_air()
40    }
41}
42impl BlockBehavior for TwistingVinesPlantBlock {
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
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        _include_data: bool,
77    ) -> Option<ItemStack> {
78        Some(ItemStack::new(&vanilla_items::TWISTING_VINES))
79    }
80
81    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
82        Some(self)
83    }
84}
85impl Bonemealable for TwistingVinesPlantBlock {
86    fn is_valid_bonemeal_target(
87        &self,
88        state: BlockStateId,
89        world: &dyn LevelReader,
90        pos: BlockPos,
91    ) -> bool {
92        self.base.is_valid_bonemeal_target(state, world, pos)
93    }
94
95    fn perform_bonemeal(
96        &self,
97        state: BlockStateId,
98        world: &Arc<World>,
99        rng: &mut dyn Rng,
100        pos: BlockPos,
101    ) {
102        self.base.perform_bonemeal(state, world, rng, pos);
103    }
104
105    fn bonemeal_action_type(&self) -> BonemealAction {
106        BonemealAction::Grower
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use steel_registry::init_vanilla_registry;
113
114    use super::*;
115    use crate::test_support::TestLevel;
116
117    #[test]
118    fn bonemeal_target_follows_connected_head() {
119        init_vanilla_registry();
120
121        let behavior = TwistingVinesPlantBlock::new(&vanilla_blocks::TWISTING_VINES_PLANT);
122        let state = vanilla_blocks::TWISTING_VINES_PLANT.default_state();
123        let open_level = TestLevel::default().with_block(
124            BlockPos::ZERO.above(),
125            vanilla_blocks::TWISTING_VINES.default_state(),
126        );
127        assert!(behavior.is_valid_bonemeal_target(state, &open_level, BlockPos::ZERO));
128
129        let blocked_level = open_level.with_block(
130            BlockPos::ZERO.above().above(),
131            vanilla_blocks::NETHERRACK.default_state(),
132        );
133        assert!(!behavior.is_valid_bonemeal_target(state, &blocked_level, BlockPos::ZERO));
134    }
135}