Skip to main content

steel_core/behavior/blocks/vegetation/
bamboo_sapling.rs

1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::{
5    blocks::{
6        BlockRef,
7        block_state_ext::BlockStateExt,
8        properties::{BambooLeaves, BlockStateProperties},
9    },
10    item_stack::ItemStack,
11    vanilla_blocks, vanilla_items,
12};
13use steel_utils::{BlockPos, BlockStateId, Direction, types::UpdateFlags};
14
15use crate::{
16    behavior::{
17        BlockBehavior, BlockPlaceContext,
18        blocks::vegetation::{BambooStalkBlock, bonemealable::Bonemealable},
19    },
20    world::{LevelReader, ScheduledTickAccess, World},
21};
22
23/// Behavior for the Bamboo Sapling Block
24#[block_behavior]
25pub struct BambooSaplingBlock {
26    block: BlockRef,
27}
28
29impl BambooSaplingBlock {
30    /// Creates a new Bamboo Sapling Behavior
31    #[must_use]
32    pub const fn new(block: BlockRef) -> Self {
33        Self { block }
34    }
35
36    /// Grows the Bamboo Sapling
37    pub fn grow(world: &Arc<World>, pos: BlockPos) {
38        world.set_block(
39            pos.above(),
40            vanilla_blocks::BAMBOO
41                .default_state()
42                .set_value(&BlockStateProperties::BAMBOO_LEAVES, BambooLeaves::Small),
43            UpdateFlags::UPDATE_ALL,
44        );
45    }
46}
47
48impl Bonemealable for BambooSaplingBlock {
49    fn get_bonemeal_age_increase(&self, _world: &Arc<World>, _rng: &mut dyn rand::Rng) -> u8 {
50        1
51    }
52
53    fn is_valid_bonemeal_target(
54        &self,
55        _state: BlockStateId,
56        world: &dyn LevelReader,
57        pos: BlockPos,
58    ) -> bool {
59        !world.is_outside_build_height(pos.above().y())
60            && world.get_block_state(pos.above()).is_air()
61    }
62
63    fn perform_bonemeal(
64        &self,
65        _state: BlockStateId,
66        world: &Arc<World>,
67        _rng: &mut dyn rand::Rng,
68        pos: BlockPos,
69    ) {
70        Self::grow(world, pos);
71    }
72}
73
74impl BlockBehavior for BambooSaplingBlock {
75    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
76        let state = self.block.default_state();
77        self.can_survive(state, context.world, context.place_pos())
78            .then_some(state)
79    }
80
81    fn update_shape(
82        &self,
83        state: BlockStateId,
84        world: &dyn ScheduledTickAccess,
85        pos: BlockPos,
86        direction: Direction,
87        _neighbor_pos: BlockPos,
88        neighbor_state: BlockStateId,
89    ) -> BlockStateId {
90        if !BambooStalkBlock::can_survive(world, pos) {
91            return vanilla_blocks::AIR.default_state();
92        }
93
94        if direction == Direction::Up && neighbor_state.get_block() == &vanilla_blocks::BAMBOO {
95            return vanilla_blocks::BAMBOO.default_state();
96        }
97
98        state
99    }
100
101    fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
102        BambooStalkBlock::can_survive(world, pos)
103    }
104
105    fn random_tick(&self, _state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
106        if rand::random_range(0..3) == 0
107            && world.get_block_state(pos.above()).is_air()
108            && world.raw_brightness(pos.above(), 0) >= 9
109        {
110            Self::grow(world, pos);
111        }
112    }
113
114    fn get_clone_item_stack(
115        &self,
116        _block: BlockRef,
117        _state: BlockStateId,
118        _include_data: bool,
119    ) -> Option<ItemStack> {
120        Some(ItemStack::new(&vanilla_items::BAMBOO))
121    }
122
123    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
124        Some(self)
125    }
126}