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