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    block: BlockRef,
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 { block }
28    }
29
30    fn can_grow_into(state: BlockStateId) -> bool {
31        state.is_air()
32    }
33
34    const fn growing_plant_body_block(&self) -> GrowingPlantBodyBlock {
35        GrowingPlantBodyBlock::new(
36            self.block,
37            Direction::Up,
38            false,
39            &vanilla_blocks::TWISTING_VINES,
40            Self::can_grow_into,
41        )
42    }
43}
44impl BlockBehavior for TwistingVinesPlantBlock {
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
74    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
75        self.growing_plant_body_block().tick(state, world, pos);
76    }
77
78    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
79        self.growing_plant_body_block()
80            .get_state_for_placement(context)
81    }
82
83    fn get_clone_item_stack(
84        &self,
85        _block: BlockRef,
86        _state: BlockStateId,
87        _include_data: bool,
88    ) -> Option<ItemStack> {
89        Some(ItemStack::new(&vanilla_items::TWISTING_VINES))
90    }
91
92    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
93        Some(self)
94    }
95}
96impl Bonemealable for TwistingVinesPlantBlock {
97    fn is_valid_bonemeal_target(
98        &self,
99        state: BlockStateId,
100        world: &dyn LevelReader,
101        pos: BlockPos,
102    ) -> bool {
103        self.growing_plant_body_block()
104            .is_valid_bonemeal_target(state, world, pos)
105    }
106
107    fn perform_bonemeal(
108        &self,
109        state: BlockStateId,
110        world: &Arc<World>,
111        rng: &mut dyn Rng,
112        pos: BlockPos,
113    ) {
114        self.growing_plant_body_block()
115            .perform_bonemeal(state, world, rng, pos);
116    }
117
118    fn bonemeal_action_type(&self) -> BonemealAction {
119        BonemealAction::Grower
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use steel_registry::init_vanilla_registry;
126
127    use super::*;
128    use crate::test_support::TestLevel;
129
130    #[test]
131    fn bonemeal_target_follows_connected_head() {
132        init_vanilla_registry();
133
134        let behavior = TwistingVinesPlantBlock::new(&vanilla_blocks::TWISTING_VINES_PLANT);
135        let state = vanilla_blocks::TWISTING_VINES_PLANT.default_state();
136        let open_level = TestLevel::default().with_block(
137            BlockPos::ZERO.above(),
138            vanilla_blocks::TWISTING_VINES.default_state(),
139        );
140        assert!(behavior.is_valid_bonemeal_target(state, &open_level, BlockPos::ZERO));
141
142        let blocked_level = open_level.with_block(
143            BlockPos::ZERO.above().above(),
144            vanilla_blocks::NETHERRACK.default_state(),
145        );
146        assert!(!behavior.is_valid_bonemeal_target(state, &blocked_level, BlockPos::ZERO));
147    }
148}