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